Introduction
The Philadelphia housing market is a dynamic and diverse landscape
influenced by various factors that impact both prices and overall market
conditions. This market reflects the economic and social dynamics of the
city and the surrounding region.It encompasses a wide range of
neighborhoods, each with its unique character, amenities, and housing
styles. The city’s diverse housing stock includes rowhouses, townhomes,
apartments, and single-family homes, catering to a variety of lifestyles
and preferences.

Significance of project
Housing prices in Philadelphia have experienced fluctuations over the
years, influenced by a combination of local and national factors.
However, it’s important to note that these prices can vary significantly
based on factors such as the neighborhood, property type, and condition
of the home.
Sale prediction algorithms are powerful tools that offer benefits to
a wide range of stakeholders in the housing market. They promote
informed decision-making, uncover inequalities, and contribute to the
overall health and fairness of the real estate market.
This project builds on this theory and aims to evaluate the various
factors affecting housing prices to create an accurate and generalizable
OLS regression model to predict the housing prices in Philadelphia.
Key challenges
Building a predictive model for property valuation is difficult given
the intricacies involved in data processing and interpretation. It
involves sorting through lots of data, choosing the right features,
understanding the nuanced influence of neighborhood characteristics,
addressing collinearity among variables, selecting appropriate modeling
techniques, evaluating model performance, ensuring interpretability, and
building a generalizable model.
Process
The process of creating this model was extremely iterative, and
involved multiple rounds of trial and error, particularly in attempts to
minimize the errors in predictions. The maps, correlation matrix and
scatterplots are all illustrative of variables that were considered,
wrangled and filtered as part of the data analysis process.
Summary of Results
The model exhibits moderate generalizability, however, the presence
of notably high errors raises concerns about its predictive accuracy. In
our process of experimentation, we were able to predict approximately
70% of the variance in sale prices, which is a reasonable level of
explanatory power for a regression model. The errors across the model
seem to be dispersed and do not display any significant spatial
auto-correlation.
This code is built upon the classwork discussed here.
R Setup and Installing Packages
This code chunk handles the essential tasks of loading necessary
packages, configuring the Census API key, defining class functions,
specifying a color palette, and managing global environment
settings.
# Loading libraries
library(tidyverse)
library(tidycensus)
library(sf)
library(kableExtra)
library(tidyr)
library(ggplot2)
library(viridis)
library(stringr)
library(tigris)
library(ggcorrplot)
library(stargazer)
library(dplyr)
library(caTools)
library(spdep)
library(caret)
library(jtools)
library(ggstance)
library(ggpubr)
library(broom.mixed)
# Setting parameters for scientific notation
options(scipen=999)
options(tigris_class = "sf")
# Functions and data directory
source("https://raw.githubusercontent.com/urbanSpatial/Public-Policy-Analytics-Landing/master/functions.r")
# Invoking color palettes to be used
palettee <- c('#d7191c','#fdae61','#ffffbf','#abd9e9','#2c7bb6')
# Registering API Key to be used
census_api_key('bf2d507651b5a621dbadd44533fb4f3deaab26bf', overwrite = TRUE)
Data Wrangling
Census, OpenData Philly and provided Dataset
Provided Dataset and Internal Variables
The code below is used to import the foundational dataset for our
model. This dataset contains information on home sales prices and
property characteristics in Philadelphia, Pennsylvania for the years
2022 and 2023. The model we are developing is designed to make
predictions about home prices and utilizes specific property attributes
from this dataset to enhance the accuracy of those predictions.
The code demonstrates data manipulation using the dplyr package to
refine the dataset by selecting specific columns and filtering rows
based on criteria. These operations are carried out because the raw
dataset is untidy and incomplete. The original dataset is further
modified by filtering rows specific to Philadelphia City and by
excluding certain columns with a majority of missing values.
# Reading Data
data <-
st_read("./data/studentData.geojson") %>%
st_transform('ESRI:102286')
# Dropping columns with no values and filtering values within Philadelphia
data <- data %>%
select(-cross_reference, -date_exterior_condition, -mailing_address_2, -mailing_care_of, -unfinished, -utility, -category_code, -category_code_description, -exempt_land, -separate_utilities, -sewer, - site_type, -house_extension, -street_direction, -suffix, -garage_type, -general_construction )%>%
filter(mailing_city_state == "PHILADELPHIA PA" )
Categorical Variables
The next step is predominantly focused on data cleaning and
transformation based on the information given in the metadata for the
dataset by the City of Philadelphia. Overall, this code aims to clean
and categorize various columns in the dataset. It does this by handling
missing values as well as assigning categories and binary values for
better analysis and modeling.
## Filtering out 9 rows where year built is not given
data <- data %>%
filter(year_built > 0 )
## Categorizing if a Basement is present
data <- data %>%
mutate(BasementPresent = case_when(basements == 'A' |
basements == 'B' |
basements == 'C' |
basements == 'D' |
basements == 'E' |
basements == 'F' |
basements == 'G' |
basements == 'H' |
basements == 'I' |
basements == 'J' ~ 1,
basements == '0' ~ 0))
# Assigning value of 0 to 'NA' rows based on description in metadata
data$BasementPresent[is.na(data$BasementPresent)] <- 0
## Categorizing Basement Type
library(dplyr)
data <- data %>%
mutate(BasementType = case_when(basements == 'A' ~ 'Full size finished',
basements == 'B' ~ 'Full size semi-finished',
basements == 'C' ~ 'Full size unfinished',
basements == 'D' ~ 'Full size unknown finish',
basements == 'E' ~ 'Partial size finished',
basements == 'F' ~ 'Partial size semi-finished',
basements == 'G' ~ 'Partial size unfinished',
basements == 'H' ~ 'Partial size unknown finish',
basements == 'I' ~ 'Unknown size finished',
basements == 'J' ~ 'Unknown size unfinished',
basements == '0' ~ 'No basement'))
# Assigning value of 0 to 'NA' rows based on description in metadata
data$BasementType[is.na(data$BasementType)] <- "No basement"
data$basements[is.na(data$basements)] <- 0
## Categorizing based on Central Air
data$central_air <- ifelse(data$central_air == 'Y', 1, 0)
## Categorizing based on Exterior Condition
data <- data %>%
mutate(ExteriorConditionType = case_when(exterior_condition == '1' |
exterior_condition == '2' |
exterior_condition == '3' ~ 'Good Condition',
exterior_condition == '4' |
exterior_condition == '5' ~ 'Average Condition',
exterior_condition == '6' ~ 'Below Average Condition',
exterior_condition == '7' ~ 'Vacant and Sealed'))
# Assigning value to single 'NA' row based on 'interior_construction' rating for same row
data$ExteriorConditionType[is.na(data$ExteriorConditionType)] <- "Good Condition"
data$exterior_condition[is.na(data$exterior_condition)] <- 3
## Categorizing based on Fireplaces
# Assigning value of 0 to 'NA' rows based on description in metadata
data$fireplaces[is.na(data$fireplaces)] <- 0
## Categorizing based on Fuel Sources
data <- data %>%
mutate(FuelType = case_when(fuel == 'A' ~ 'Natural Gas Powered',
fuel == 'B' ~ 'Oil Powered',
fuel == 'C' ~ 'Electric Powered',
fuel == 'E' ~ 'Solar Powered',
fuel == 'G' ~ 'Fuel Source Unknown'))
# Assigning value of Unknown/G to 'NA' rows based on description in metadata
data$FuelType[is.na(data$FuelType)] <- "Fuel Source Unknown"
data$fuel[is.na(data$fuel)] <- "G"
## Categorizing based on Garage Presence
data <- data %>%
mutate(GaragePresent = case_when(garage_spaces == '0' ~ 0,
garage_spaces == '1' |
garage_spaces == '2' |
garage_spaces == '3' ~ 1))
# Assigning value of 0 to 'NA' rows based on description in metadata
data$GaragePresent[is.na(data$GaragePresent)] <- 0
data$garage_spaces[is.na(data$garage_spaces)] <- 0
## Categorizing based on Garage Types
data <- data %>%
mutate(GarageType = case_when(garage_spaces == '0' ~ 'No Garage',
garage_spaces == '1' ~ 'Single Garage',
garage_spaces == '2' |
garage_spaces == '3' ~ 'Multiple Garages'))
# Assigning value of 0 to 'NA' rows based on description in metadata
data$GarageType[is.na(data$GarageType)] <- "No Garage"
data$garage_spaces[is.na(data$garage_spaces)] <- 0
data <- data %>%
mutate(InteriorConditionType = case_when( interior_condition == '0' |
interior_condition == '1' |
interior_condition == '2' |
interior_condition == '3' ~ 'Good Condition',
interior_condition == '4' ~ 'Average Condition',
interior_condition == '5' ~ 'Below Average Condition',
interior_condition == '6' |
interior_condition == '7' ~ 'Vacant and/or Sealed'))
# Assigning values to 0 or 'NA' rows based on exterior condition since interior and exterior conditions are equal in almost all cases
data$InteriorConditionType[is.na(data$InteriorConditionType)] <- c(data$ExteriorConditionType)
## Categorizing based on View Type
data <- data %>%
mutate(ViewType = case_when(view_type == '0' ~ 'Nature of View Unknown',
view_type == 'I' ~ 'Typical View',
view_type == 'A' ~ 'Skyline View',
view_type == 'B' ~ 'River View',
view_type == 'C' ~ 'Park View',
view_type == 'D' ~ 'Commercial Area View',
view_type == 'E' ~ 'Industrial Area View',
view_type == 'H' ~ 'View of Landmark'))
# Assigning value to NA rows to nature of view unknown
data$ViewType[is.na(data$ViewType)] <- "Nature of View Unknown"
data$view_type[is.na(data$view_type)] <- 0
## Categorizing based on Topography Type
data <- data %>%
mutate(TopographyType = case_when(topography == 'A' ~ 'Above Street Level Topography',
topography == 'B' ~ 'Below Street Level Topography',
topography == 'C' ~ 'Flood Plain Topography',
topography == 'D' ~ 'Rocky Topography',
topography == 'E' ~ 'Other Topography',
topography == 'F' ~ 'Level Topography'))
# Assigning value to NA rows to nature of view unknown
data$TopographyType[is.na(data$TopographyType)] <- "Topography Unknown"
data$topography[is.na(data$topography)] <- 0
## Categorizing based on Parcel Type
data <- data %>%
mutate(ParcelType = case_when(parcel_shape == 'A' ~ 'Irregular Parcel',
parcel_shape == 'B' ~ 'Grossly Irregular Parcel',
parcel_shape == 'C' ~ 'Triangular Parcel',
parcel_shape == 'D' ~ 'Long Narrow Parcel',
parcel_shape == 'E' ~ 'Rectangular Parcel'))
# Assigning value to NA rows to nature of view unknown
data$ParcelType[is.na(data$ParcelType)] <- "Parcel Type Unknown"
data$parcel_shape[is.na(data$parcel_shape)] <- 0
Imputing Missing Data
An initial analysis of the dataset indicated missing values for a
number of rows within important factors that inform sale prices such as
number of bedrooms, number of bathrooms, and number of rooms. A linear
regression model is used to predict these missing values based on the
relationships observed in the available data.
In the given dataset, a strong relation is observed between number of
rooms and total livable area. Here, the linear relationship between
these two factors are studied across the dataset and projected to the
rows with the missing values. The assumption here is that, when plotted
on a graph, the missing values will have the same slope and intercept as
the rest of the dataset.
This imputing practice is extended to other fields with missing
values such as number of bathrooms and number of bedrooms.
Bedrooms
## Imputing values for missing values of number of bedrooms based on total livable area
# Dropping 32 values of total livable area which are 0 for better prediction
data <- data %>%
filter(total_livable_area > 0 )
# Step 1 - Creating an index of 0 and 1 values for rows that have values for number of bathrooms and rows that do not
data <- data %>%
mutate(BedroomIndex = case_when(number_of_bedrooms >= 1 ~ 1,
number_of_bedrooms < 1 ~ 0))
data$BedroomIndex[is.na(data$BedroomIndex)] <- 0
# Step 2 - Creating a linear regression model relating number of bedrooms and total liveable area
lm(number_of_bedrooms ~ total_livable_area, data=data)
# Step 3 - Imputing new values for missing values of number of bedrooms using regression results
for(i in 1:nrow(data))
{
if (data$BedroomIndex[i] == 0)
{
data$number_of_bedrooms[i] = 2.1605650 + 0.0003057*data$total_livable_area[i]
}
}
data$number_of_bedrooms <- round(data$number_of_bedrooms, digits=0)
Rooms
## Imputing values for missing values of number of rooms based on total livable area
# Step 1 - Creating an index of 0 and 1 values for rows that have values for number of rooms and rows that do not
data <- data %>%
mutate(RoomIndex = case_when(number_of_rooms >= 1 ~ 1,
number_of_rooms < 1 ~ 0))
data$RoomIndex[is.na(data$RoomIndex)] <- 0
# Step 2 - Creating a linear regression model relating number of rooms and total livable area
lm(number_of_rooms ~ total_livable_area, data=data)
# Step 3 - Imputing new values for missing values of number of rooms using regression results
for(i in 1:nrow(data))
{
if (data$RoomIndex[i] == 0)
{
data$number_of_rooms[i] = 4.316503 + 0.001319*data$total_livable_area[i]
}
}
data$number_of_rooms <- round(data$number_of_rooms, digits=0)
Bathrooms
## Imputing values for missing values of number of bathrooms based on total livable area
# Step 1 - Creating an index of 0 and 1 values for rows that have values for number of bathrooms and rows that do not
data <- data %>%
mutate(BathroomIndex = case_when(number_of_bathrooms >= 1 ~ 1,
number_of_bathrooms < 1 ~ 0))
data$BathroomIndex[is.na(data$BathroomIndex)] <- 0
# Step 2 - Creating a linear regression model relating number of bathrooms and total livable area
lm(number_of_bathrooms ~ total_livable_area, data=data)
# Step 3 - Imputing new values for missing values of number of bathrooms using regression results
for(i in 1:nrow(data))
{
if (data$BathroomIndex[i] == 0)
{
data$number_of_bathrooms[i] = 0.6426310 + 0.0003283*data$total_livable_area[i]
}
}
data$number_of_bathrooms <- round(data$number_of_bathrooms, digits=0)
Creating New Variables
Post cleaning, two new variables are created within the dataset to
evaluate the price per square foot within sold properties as well as the
age of the sold properties.
data <-
data %>%
mutate(PricePerSqft = (sale_price/total_livable_area))
data <-
data %>%
mutate(BuildingAge = (2023 - (year_built)))
Dropping Columns and Outliers
As the last step, the foundational dataset carrying information about
the properties is filtered to only hold the indicative internal
variables which may play a role in influencing the home price value. To
achieve better accuracy and fewer errors in prediction, outlier values
in the dataset are eliminated to remove any significant skews in the
dataset.
data <- data %>%
select(objectid, assessment_date, BuildingAge, year_built, building_code, building_code_description, pin, building_code_new, building_code_description_new, census_tract, geographic_ward, zoning, location, street_name, street_code, street_designation, zip_code, house_number, depth, frontage, central_air, fireplaces, fuel, FuelType, basements, BasementPresent, BasementType, garage_spaces, GaragePresent, GarageType, exterior_condition, ExteriorConditionType, interior_condition, InteriorConditionType, number_of_bathrooms, number_of_bedrooms, number_of_rooms, number_stories, total_livable_area, view_type, ViewType, topography, TopographyType, parcel_shape, ParcelType, sale_date, sale_year, sale_price, PricePerSqft, musaID, toPredict, geometry )
data <- data %>%
filter(number_of_bedrooms <10, number_of_rooms<15, ((number_of_bathrooms+number_of_bedrooms) < number_of_rooms), PricePerSqft<1500, total_livable_area<10000)
Census Data
After cleaning the primary dataset and importing data on surrounding
physical amenities, the next step is gathering relevant census data to
understand demographic conditions pertaining to these data points. The
provided R code chunk fetches and processes data from the American
Community Survey (ACS) for Philadelphia census tracts in the year 2021.
It retrieves various demographic and socioeconomic variables for these
tracts and performs some data transformations. The year chosen for
analysis is 2021 to factor recovery from Covid in order to make more
accurate predictions.
A list of variables including population, income, housing-related
information, education, poverty levels, and racial/ethnic demographics
are gathered and transformed to be projected in the ESRI:102728
coordinate system. This information is stored in a new dataset -
‘tracts21’.
acs_variable_list.2021 <- load_variables(2021, #year
"acs5", #five year ACS estimates
cache = TRUE)
# 2021, A
# Retrieve ACS data for Philadelphia tracts in 2020
tracts21 <- get_acs(
geography = "tract",
variables = c(
"B01003_001", # Total Population
"B19013_001", # Median Household Income
"B25058_001", # Median Rent
"B25008_002", # Owner-Occupied Units
"B25008_003", # Renter-Occupied Units
"B07001_032", # Same House 75 Years Ago
"B07001_017", # Same House 1 Year Ago
"B25088_003", # Median Selected Monthly Owner Costs (homes without a mortgage)
"B25088_002", # Median Selected Monthly Owner Costs (homes with a mortgage)
"B25064_001", # Median Gross Rent (rent and utilities)
"B25117_001", # Percentage of Housing Units with heat
"B15003_022", # Educational Attainment: Bachelor's Degree
"B17001_002", # Percentage of Population Below the Poverty Level
"B28002_004", # Percentage of Housing Units with High-Speed Internet
"B25044_003", # Percentage of Housing Units with No Vehicle Available
"B02001_002", # Race and Ethnicity: White Alone
"B02001_003", # Race and Ethnicity: Black or African American Alone
"B03001_003" # Hispanic or Latino Origin of Population
),
year = 2021,
state = "PA",
county = "Philadelphia",
geometry = TRUE,
output = "wide"
)%>%
select(-NAME, -ends_with("M")) %>%
rename(totalpop = B01003_001E, # Total Population
med_income = B19013_001E, # Median Household Income
med_rent = B25058_001E, # Median Rent
owner_units = B25008_002E, # Owner-Occupied Units
renter_units = B25008_003E, # Renter-Occupied Units
same_house_75 = B07001_032E, # Same House 75 Years Ago
same_house_1 = B07001_017E, # Same House 1 Year Ago
monthly_costs_no_mortgage = B25088_003E, # Median Selected Monthly Owner Costs (homes without a mortgage)
monthly_costs_with_mortgage = B25088_002E, # Median Selected Monthly Owner Costs (homes with a mortgage)
med_gross_rent = B25064_001E, # Median Gross Rent (rent and utilities)
housing_units_with_heat = B25117_001E, # Percentage of Housing Units with heat
edu_bachelors = B15003_022E, # Educational Attainment: Bachelor's Degree
pop_below_poverty = B17001_002E, # Percentage of Population Below the Poverty Level
housing_units_high_speed_internet = B28002_004E, # Percentage of Housing Units with High-Speed Internet
housing_units_no_vehicle = B25044_003E, # Percentage of Housing Units with No Vehicle Available
race_white = B02001_002E, # Race and Ethnicity: White Alone
race_black = B02001_003E, # Race and Ethnicity: Black or African American Alone
hispanic_latino = B03001_003E # Race and Ethnicity: Hispanic or Latino
)
# Transform the data to ESRI:102728 projection
tracts21 <- tracts21 %>% st_transform(st_crs(data))
Open Data philly
To begin constructing a predictive model for the Philadelphia housing
market, additional external variables are incorporated. These variables
examine the proximity of various amenities and public services to the
homes listed in the foundational dataset. The following code imports
data related to these potentially influential amenities, including
information about the locations of public and private schools, colleges,
city landmarks, playgrounds, trails, tennis courts, pools, transit stops
and police stations. Additionally, information on potential home value
inhibitors such as litter, proximity to the floodplain and instances of
gun violence are also brought in. These datasets are sourced from Open Data Philly.
# Nearest Schools
## Adding data on Philadelphia Schools
PhillySchools <-
st_read("./data/Schools.geojson") %>%
st_transform(st_crs(tracts21))
PhillyPvtSchools <-
st_read("./data/Schools.geojson") %>%
filter(TYPE_SPECIFIC == "PRIVATE") %>%
st_transform(st_crs(tracts21))
## Mapping nearest school
nearest_school <- sf::st_nearest_feature(data, PhillySchools)
nearest_pvt_school <- sf::st_nearest_feature(data, PhillyPvtSchools)
## Converting schools to rsgeo geometries
x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillySchools)
## Calculating distance
data$dist_to_nearest_school <- rsgeo::distance_euclidean_pairwise(x, y[nearest_school])
## Converting private schools to rsgeo geometries
x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyPvtSchools)
## Calculating distance
data$dist_to_nearest_pvt_school <- rsgeo::distance_euclidean_pairwise(x, y[nearest_pvt_school])
# Nearest Colleges
## Adding data on Philadelphia Colleges
PhillyColleges <-
st_read("./data/Universities_Colleges.geojson")%>%
st_transform(st_crs(tracts21))
## Mapping nearest college
nearest_college <- sf::st_nearest_feature(data, PhillyColleges)
## Converting to rsgeo geometries
x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyColleges)
## Calculating distance
data$dist_to_nearest_college <- rsgeo::distance_euclidean_pairwise(x, y[nearest_college])
# Nearest Landmarks
## Adding data on Philadelphia landmarks
PhillyLandmarks <-
st_read("https://services.arcgis.com/fLeGjb7u4uXqeF9q/arcgis/rest/services/Landmark_Points/FeatureServer/0/query?outFields=*&where=1%3D1&f=geojson")%>%
st_transform(st_crs(tracts21))
## Mapping nearest landmark
nearest_landmark <- sf::st_nearest_feature(data, PhillyLandmarks)
## Converting to rsgeo geometries
x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyLandmarks)
## Calculating distance
data$dist_to_nearest_landmark <- rsgeo::distance_euclidean_pairwise(x, y[nearest_landmark])
# Nearest Commercial Corridors
## Adding data on Philadelphia Commercial Corridors
PhillyComCorr <-
st_read("./data/Commercial_Corridors.geojson") %>%
st_transform(st_crs(tracts21))
## Is it within the commercial corridor?
data$within_com_corr <- ifelse(st_within(data, PhillyComCorr), 1, 0)
data <- data %>%
mutate(within_com_corr = ifelse(is.na(within_com_corr), 0, 1))
## Mapping nearest commercial corridor
nearest_corridor <- sf::st_nearest_feature(data, PhillyComCorr)
## Converting to rsgeo geometries
x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyComCorr)
## Calculating distance
data$dist_to_comm_corr <- rsgeo::distance_euclidean_pairwise(x, y[nearest_corridor])
# Litter Index
## Adding data on Philadelphia's Litter Index
PhillyLitter <-
st_read("./data/Litter_Index.geojson") %>%
st_transform(st_crs(tracts21))
## Joining the litter score
data <-
st_join(data,(PhillyLitter %>%
select(-OBJECTID, -Shape__Area, -Shape__Length )%>%
rename(litter = SCORE)))
#Nearest Flood Plains
## Adding data on Philadelphia's Flood Plain
PhillyFlood <-
st_read("./data/FEMA_100_flood_Plain.geojson") %>%
st_transform(st_crs(tracts21))
## Is it within the floodplain?
data$within_flood <- ifelse(st_within(data, PhillyFlood), 1, 0)
data <- data %>%
mutate(within_flood = ifelse(is.na(within_flood), 0, 1))
## Mapping nearest floodplain
nearest_floodplain <- sf::st_nearest_feature(data, PhillyFlood)
## Converting to rsgeo geometries
x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyFlood)
## Calculating distance
data$dist_to_floodplain <- rsgeo::distance_euclidean_pairwise(x, y[nearest_floodplain])
# Nearest Transit Stops
## Adding data on Philadelphia's Transit Stops
el <- st_read("https://opendata.arcgis.com/datasets/8c6e2575c8ad46eb887e6bb35825e1a6_0.geojson")
Broad_St <- st_read("https://opendata.arcgis.com/datasets/2e9037fd5bef406488ffe5bb67d21312_0.geojson")
PhillySeptaStops <-
rbind(
el %>%
mutate(Line = "El") %>%
dplyr::select(Station, Line),
Broad_St %>%
mutate(Line ="Broad_St") %>%
dplyr::select(Station, Line)) %>%
st_transform(st_crs(tracts21))
## Mapping nearest transit stop
nearest_station <- sf::st_nearest_feature(data, PhillySeptaStops)
## Converting to rsgeo geometries
x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillySeptaStops)
## Calculating distance
data$dist_to_nearest_station <- rsgeo::distance_euclidean_pairwise(x, y[nearest_station])
# Nearest Trails
## Adding data on Philadelphia Trails
PhillyTrails <-
st_read("./data/PPR_Trails.geojson")%>%
st_transform(st_crs(tracts21))
## Mapping nearest trail
nearest_trail <- sf::st_nearest_feature(data, PhillyTrails)
## Converting to rsgeo geometries
x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyTrails)
## Calculating distance
data$dist_to_nearest_trail <- rsgeo::distance_euclidean_pairwise(x, y[nearest_trail])
# Nearest Tennis Courts
## Adding data on Philadelphia Tennis Courts
PhillyTennisCourts <-
st_read("./data/PPR_Tennis_Courts.geojson")%>%
st_transform(st_crs(tracts21))
## Mapping nearest tennis court
nearest_tenniscourt <- sf::st_nearest_feature(data, PhillyTennisCourts)
## Converting to rsgeo geometries
x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyTennisCourts)
## Calculating distance
data$dist_to_nearest_tenniscourt <- rsgeo::distance_euclidean_pairwise(x, y[nearest_tenniscourt])
# Nearest Playgrounds
## Adding data on Philadelphia Playgrounds
PhillyPlaygrounds <-
st_read("./data/PPR_Playgrounds.geojson")%>%
st_transform(st_crs(tracts21))
## Mapping nearest playground
nearest_playground <- sf::st_nearest_feature(data, PhillyPlaygrounds)
## Converting to rsgeo geometries
x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyPlaygrounds)
## Calculating distance
data$dist_to_nearest_playground <- rsgeo::distance_euclidean_pairwise(x, y[nearest_playground])
# Nearest Pools
## Adding data on Philadelphia Pools
PhillyPools <-
st_read("./data/PPR_Swimming_Pools.geojson")%>%
st_transform(st_crs(tracts21))
## Mapping nearest pool
nearest_pool <- sf::st_nearest_feature(data, PhillyPools)
## Converting to rsgeo geometries
x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyPools)
## Calculating distance
data$dist_to_nearest_pool <- rsgeo::distance_euclidean_pairwise(x, y[nearest_pool])
# Nearest Police Stations
## Adding data on Philadelphia Police Stations
PhillyPoliceStations <-
st_read("./data/Police_Stations.geojson")%>%
st_transform(st_crs(tracts21))
## Mapping nearest police station
nearest_police_station <- sf::st_nearest_feature(data, PhillyPoliceStations)
## Converting to rsgeo geometries
x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyPoliceStations)
## Calculating distance
data$dist_to_nearest_police_station <- rsgeo::distance_euclidean_pairwise(x, y[nearest_police_station])
A distance analysis is performed for each of the above amenities in
relation to our home value dataset to identify the nearest amenity of
each kind to each house in the original dataset. This analysis is
conducted as a preparatory step, the distances derived from this
analysis are studied next to determine if these spatial factors play a
role in influencing home prices.
K Nearest Neighbors
In order to evaluate the instances of gun violence around a listed
home, the method of k-Nearest Neighbors Analysis is used. In this case,
a higher value of k=5 is preferred to account for the spatial
distribution of the instances in relation to the homes and to ensure
that there are enough neighbor observations for each listed home for
better balance across the dataset. A higher k value is also preferred
for the robustness and higher generalizability it offers during
prediction.
# Philadelphia Shootings
## Adding data on Philadelphia Shootings
PhillyShootings <-
st_read("./data/shootings2021.geojson")%>%
dplyr::select("date_", "point_x", "point_y", "geometry")%>%
st_transform(st_crs(data))%>%
na.omit()
## Reading layer `shootings2021' from data source
## `D:\Fall_2023\PPA\Midterm\data\shootings2021.geojson' using driver `GeoJSON'
## replacing null geometries with empty geometries
## Simple feature collection with 2341 features and 21 fields (with 11 geometries empty)
## Geometry type: POINT
## Dimension: XY
## Bounding box: xmin: -75.26654 ymin: 39.90286 xmax: -74.95936 ymax: 40.1173
## Geodetic CRS: WGS 84
##calculating distance to nearest neighbors
library(sf)
library(spatstat)
library(class)
data_ppp <- as.ppp(data)
PhillyShootings_ppp <- as.ppp(PhillyShootings)
data$crime_nn5 <- nncross(data_ppp, PhillyShootings_ppp, k = 5)
tracts21 <-
tracts21 %>%
mutate(PctWhite = ((race_white/totalpop)*100),
PctBlack = ((race_black/totalpop)*100),
PctHispanic = ((hispanic_latino/totalpop)*100),
PctBachelors = ((edu_bachelors/totalpop)*100),
PctPoverty = ((pop_below_poverty/totalpop)*100))
Joining Data
In order to begin conducting exploratory analyses of the relations
within the dataset, the foundational dataset is joined with the datasets
containing relevant spatial and demographic attributes.
# joining census data
data <-
st_join(data, tracts21)
#saving for prediction
data_og <- data
data <- data %>%
filter(sale_price > 0)
Exploratory Data Analysis
Provided below are the summary statistics for the different types of
variables considered (internal, spatial, and demographic). The
exploration further looks at the distribution of non-numeric categorical
variables through bar plots. It finally inspects the dependence of ‘home
sale price’ on other continuous independent variables including total
area, median income, and racial distribution among others.
Summary Statistics: Internal Variables
These statistics provide an overview of the central tendency, spread,
and range of the internal variables for Philadelphia homes, which
include sale prices, price per square foot, living area size, year
built, number of rooms, number of bathrooms, and number of bedrooms.
InternalVariables <- data
InternalVariables <- st_drop_geometry(InternalVariables)
InternalVariables <- InternalVariables %>%
dplyr::select("sale_price", "PricePerSqft", "total_livable_area", "year_built", "number_of_rooms", "number_of_bathrooms", "number_of_bedrooms")
stargazer(as.data.frame(InternalVariables), type="text", digits=1, title = "Descriptive Statistics for Philadelphia Homes Internal Variables (Figure 4.1)", out = "Training_PHLInternal.txt")
##
## Descriptive Statistics for Philadelphia Homes Internal Variables (Figure 4.1)
## ===============================================================
## Statistic N Mean St. Dev. Min Max
## ---------------------------------------------------------------
## sale_price 18,040 282,857.8 228,937.6 11,000 5,900,000
## PricePerSqft 18,040 209.3 118.7 7.2 1,443.2
## total_livable_area 18,040 1,334.0 516.8 186 7,391
## year_built 18,040 1,937.8 29.8 1,750 2,024
## number_of_rooms 18,040 6.1 0.7 3 14
## number_of_bathrooms 18,040 1.2 0.4 1 5
## number_of_bedrooms 18,040 2.9 0.6 1 8
## ---------------------------------------------------------------
It is observed that the average sale price of homes in Philadelphia
is approximately 282,857.8 USD. The sale prices vary quite widely with a
high standard deviation, indicating significant dispersion in prices. It
is also observed that the average livable area in Philadelphia is
approximately 1334 sqft with the price per square foot for homes
positioned at about 209.3 USD.
The average age for a house in Philadelphia is approximately 85 years
indicating a large share of the market is held by homes constructed over
a century ago.
Finally, homes in Philadelphia on average seem to have 6 rooms
overall with approximately 3 bedrooms and 1 bathroom.
Summary Statistics: Demographic Variables
These statistics provide an overview of the central tendency, spread,
and range of the internal variables for Philadelphia homes, which
include median income, percentage of white population, percentage of
black population, percentage of hispanic population, percentage of
population with a bachelor’s degree and percentage of population living
in poverty.
DemographicVariables <- data
DemographicVariables <- st_drop_geometry(DemographicVariables)
DemographicVariables <- DemographicVariables %>%
dplyr::select("PctWhite", "PctBlack", "PctHispanic", "PctBachelors", "PctPoverty", "med_income")
stargazer(as.data.frame(DemographicVariables), type="text", digits=1, title = "Descriptive Statistics for Philadelphia Homes Demographic Variables (Figure 4.1)", out = "Training_PHLSpatial.txt")
##
## Descriptive Statistics for Philadelphia Homes Demographic Variables (Figure 4.1)
## ====================================================
## Statistic N Mean St. Dev. Min Max
## ----------------------------------------------------
## PctWhite 18,039 44.1 30.6 0.0 95.7
## PctBlack 18,039 35.7 33.2 0.0 98.9
## PctHispanic 18,039 15.4 18.8 0.0 91.7
## PctBachelors 18,039 14.1 10.0 0.0 42.3
## PctPoverty 18,039 20.4 13.1 0.0 65.1
## med_income 17,882 61,320.0 28,995.2 11,955 210,322
## ----------------------------------------------------
The table presents descriptive statistics for demographic variables
related to Philadelphia homes, offering insights into the
characteristics of the population in different areas.
On average, approximately 44.1% of the population in the observed
areas is White. The range of White population percentages spans from
0.0% to 95.7%, indicating a wide diversity in racial composition across
different areas of Philadelphia. The same holds true for Black and
Hispanic population percentages indicating the presence of
White-majority, Black majority and Hispanic majority neighborhoods in
different parts of Philadelphia. On average, about 14.1% of the
population in the observed areas holds a Bachelor’s degree or
higher.
The average median household income in the observed areas is
$61,320.0. However, approximately 20.4% of the population in the
observed areas falls below the poverty level, on average.
Summary Statistics: Spatial Variables
These statistics provide an overview of the central tendency, spread,
and range of the internal variables for Philadelphia homes, which
include distances to key nearest amenities and threatening activities
including nearest schools, nearest private schools, nearest landmarks,
nearest commercial corridors, nearest trails, nearest police stations,
and nearest crime instances.
SpatialVariables <- data
SpatialVariables <- st_drop_geometry(SpatialVariables)
SpatialVariables <- SpatialVariables %>%
dplyr::select("dist_to_nearest_school", "dist_to_nearest_pvt_school", "dist_to_nearest_landmark", "dist_to_comm_corr", "dist_to_nearest_station", "dist_to_nearest_trail", "dist_to_nearest_police_station","crime_nn5")
stargazer(as.data.frame(SpatialVariables), type="text", digits=1, title = "Descriptive Statistics for Philadelphia Homes Spatial Variables (Figure 4.1)", out = "Training_PHLSpatial.txt")
##
## Descriptive Statistics for Philadelphia Homes Spatial Variables (Figure 4.1)
## ====================================================================
## Statistic N Mean St. Dev. Min Max
## --------------------------------------------------------------------
## dist_to_nearest_school 18,040 351.4 202.0 9.9 1,799.4
## dist_to_nearest_pvt_school 18,040 859.3 555.4 24.4 3,784.1
## dist_to_nearest_landmark 18,040 441.6 259.8 5.8 2,213.8
## dist_to_comm_corr 18,040 207.7 201.7 0.0 2,252.1
## dist_to_nearest_station 18,040 2,683.8 2,809.9 35.9 13,808.6
## dist_to_nearest_trail 18,040 1,403.1 927.2 9.6 4,993.5
## dist_to_nearest_police_station 18,040 1,652.1 959.3 29.4 5,378.6
## --------------------------------------------------------------------
These statistics offer insights into the spatial characteristics of
Philadelphia neighborhoods where the “Mean” in each case represents the
average distance to a specific amenity from a listed property.
Plotting Categorical Variables
The following exploratory analysis plot, titled “Price as a function
of Categorical Variables,” is a set of grouped bar charts. Each chart
corresponds to a selected categorical variable, and within each chart,
different categories of that variable are compared based on their
influence on the sale price of homes. This visualization helps assess
how each categorical variable relates to home sale prices and allows for
visual comparison across categories. This allows a clear comparison of
multiple categorical variables at once, making it easier to identify
trends and patterns in the data.
CategoricalVariables <- data
CategoricalVariables <- st_drop_geometry(CategoricalVariables)
CategoricalVariables <- CategoricalVariables %>%
dplyr::select("BasementType", "GarageType", "ViewType", "ExteriorConditionType", "InteriorConditionType", "building_code_description_new","sale_price")
CategoricalVariables %>%
gather(CategoricalVariables, Value, -sale_price) %>%
ggplot(aes(Value, sale_price)) +
geom_bar(stat = "identity", position = "dodge") +
facet_wrap(~CategoricalVariables, scales = "free", ncol=2) +
labs(title = "Price as a function of Categorical Variables") +
plotTheme() + theme(axis.text.x = element_text(angle = 45, hjust = 1))

Exploratory Mapping and Analysis
Mapping Internal Variables
##Mapping Internal Variables
# Mapping sale price
ggplot() +
geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
geom_sf(data = data, aes(colour = q5(sale_price)),
show.legend = "point", size = .75) +
scale_colour_manual(values = palettee,
labels=qBr(data, "sale_price"),
name="Quintile Breaks:\n Sale Price",
na.value = NA) +
labs(title="Properties by Sale Price", subtitle = "Philadelphia 2022-2023",
caption="Figure 3.1.1") +
mapTheme()

# Mapping Size
ggplot() +
geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
geom_sf(data = data, aes(colour = q5(total_livable_area)),
show.legend = "point", size = .75) +
scale_colour_manual(values = palettee,
labels=qBr(data, "total_livable_area"),
name="Quintile Breaks:\nArea in Sq Ft",
na.value = NA) +
labs(title="Properties by Size", subtitle = "Philadelphia 2022-2023",
caption="Figure 3.1.2") +
mapTheme()

# Mapping Interior Condition
ggplot() +
geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
geom_sf(data = data, aes(colour = (InteriorConditionType)),
show.legend = "point", size = .5) +
scale_colour_manual(values = palettee, name="Interior Condition",
na.value = NA) +
labs(title="Properties by Internal Condition", subtitle = "Philadelphia 2022-2023",
caption="Figure 3.1.3") +
mapTheme()

Mapping Spatial Variables
## Mapping Spatial Variables
# Mapping properties around schools
ggplot() +
geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
geom_sf(data = data, aes(colour = q5(sale_price)),
show.legend = "point", size = .75, alpha=0.3) +
geom_sf(data = PhillySchools, colour = "black", size = 1.5, alpha=0.6) +
scale_colour_manual(values = palettee,
labels=qBr(data, "sale_price"),
name="Quintile Breaks:\nSale Prices",
na.value = NA) +
labs(title="Properties Around Schools", subtitle = "Philadelphia 2022-2023",
caption="Figure 3.2.1") +
mapTheme()

# Mapping properties around commercial corridors
ggplot() +
geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
geom_sf(data = data, aes(colour = q5(sale_price)),
show.legend = "point", size = .75, alpha=0.3) +
geom_sf(data = PhillyComCorr, colour = "black", fill="black", alpha=0.6) +
scale_colour_manual(values = palettee,
labels=qBr(data, "sale_price"),
name="Quintile Breaks:\nSale Prices",
na.value = NA) +
labs(title="Properties Around Commercial Corridors", subtitle = "Philadelphia 2022-2023",
caption="Figure 3.2.2") +
mapTheme()

# Mapping properties around landmarks
ggplot() +
geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
geom_sf(data = data, aes(colour = q5(sale_price)),
show.legend = "point", size = .75, alpha=0.3) +
geom_sf(data = PhillyLandmarks, colour = "black", fill="black", size = .75, alpha=0.6) +
scale_colour_manual(values = palettee,
labels=qBr(data, "sale_price"),
name="Quintile Breaks:\nSale Prices",
na.value = NA) +
labs(title="Properties Around Landmarks", subtitle = "Philadelphia 2022-2023",
caption="Figure 3.2.3") +
mapTheme()

# Mapping properties around floodplains
ggplot() +
geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
geom_sf(data = data, aes(colour = q5(sale_price)),
show.legend = "point", size = .75, alpha=0.3) +
geom_sf(data = PhillyFlood, colour = "black", fill="black", size = .75, alpha=0.6) +
scale_colour_manual(values = palettee,
labels=qBr(data, "sale_price"),
name="Quintile Breaks:\nSale Prices",
na.value = NA) +
labs(title="Properties Around Flood Plain", subtitle = "Philadelphia 2022-2023",
caption="Figure 3.2.4") +
mapTheme()

#library(gridExtra)
#grid.arrange(
#b1,
#b2,
#b3,
#b4,
#nrow = 2,
#widths=c(4,4),
#top = "Title of the page"
#)
Mapping Demographic Variables
## Mapping Demographic Variables
# Mapping median income around sold properties
ggplot() +
geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
geom_sf(data = tracts21, aes(fill = q5(med_income)), color = "transparent", alpha=0.5) +
scale_fill_brewer(palette = "BuPu",
labels = qBr(tracts21, "med_income"),
name = "Median Income\nQuintile Breaks") +
geom_sf(data = data, aes(colour = q5(sale_price)),
show.legend = "point", size = .2) +
scale_colour_manual(values = palettee,
labels=qBr(data, "sale_price"),
name="Quintile Breaks:\nSale Prices",
na.value = NA) +
labs(title="Median Income Around Sold Properties", subtitle = "Philadelphia 2022-2023",
caption="Figure 3.3.1")+
mapTheme()

# Mapping white population around sold properties
ggplot() +
geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
geom_sf(data = tracts21, aes(fill = q5(PctWhite)), color = "transparent", alpha=0.5) +
scale_fill_brewer(palette = "BuPu",
labels = qBr(tracts21, "PctWhite"),
name = "% White Population\nQuintile Breaks") +
geom_sf(data = data, aes(colour = q5(sale_price)),
show.legend = "point", size = .2) +
scale_colour_manual(values = palettee,
labels=qBr(data, "sale_price"),
name="Quintile Breaks:\nSale Prices",
na.value = NA) +
labs(title="% White Population Around Sold Properties", subtitle = "Philadelphia 2022-2023",
caption="Figure 3.3.2")+
mapTheme()

# Mapping black population around sold properties
ggplot() +
geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
geom_sf(data = tracts21, aes(fill = q5(PctBlack)), color = "transparent", alpha=0.5) +
scale_fill_brewer(palette = "BuPu",
labels = qBr(tracts21, "PctBlack"),
name = "% Black Population\nQuintile Breaks") +
geom_sf(data = data, aes(colour = q5(sale_price)),
show.legend = "point", size = .2) +
scale_colour_manual(values = palettee,
labels=qBr(data, "sale_price"),
name="Quintile Breaks:\nSale Prices",
na.value = NA) +
labs(title="% Black Population Around Sold Properties", subtitle = "Philadelphia 2022-2023",
caption="Figure 3.3.3")+
mapTheme()

# Mapping population with bachelor's degree around sold properties
ggplot() +
geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
geom_sf(data = tracts21, aes(fill = q5(PctBachelors)), color = "transparent", alpha=0.5) +
scale_fill_brewer(palette = "BuPu",
labels = qBr(tracts21, "PctBachelors"),
name = "% Bachelor's Degree\nQuintile Breaks") +
geom_sf(data = data, aes(colour = q5(sale_price)),
show.legend = "point", size = .2) +
scale_colour_manual(values = palettee,
labels=qBr(data, "sale_price"),
name="Quintile Breaks:\nSale Prices",
na.value = NA) +
labs(title="% Population with Bachelor's Degree Around Sold Properties", subtitle = "Philadelphia 2022-2023",
caption="Figure 3.3.4")+
mapTheme()

# Mapping population in poverty around sold properties
ggplot() +
geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
geom_sf(data = tracts21, aes(fill = q5(PctPoverty)), color = "transparent", alpha=0.5) +
scale_fill_brewer(palette = "BuPu",
labels = qBr(tracts21, "PctPoverty"),
name = "% Pop in Poverty\nQuintile Breaks") +
geom_sf(data = data, aes(colour = q5(sale_price)),
show.legend = "point", size = .2) +
scale_colour_manual(values = palettee,
labels=qBr(data, "sale_price"),
name="Quintile Breaks:\nSale Prices",
na.value = NA) +
labs(title="% Population in Poverty Around Sold Properties", subtitle = "Philadelphia 2022-2023",
caption="Figure 3.3.5")+
mapTheme()

Scatterplots and Correlations
Scatterplots
The following code generates a set of scatterplots to explore the
relationships between the sale price of homes and various continuous
variables. The data is first filtered to exclude observations where the
sale_price is less than 10,000,000 to remove any outliers. For the
remaining data, a set of scatterplots is created. Each scatterplot
examines how the sale_price relates to different continuous variables.
The scatterplots help visualize the relationships between the sale price
of homes and each of the continuous variables, as well as the direction
and strength of these relationships. The regression lines indicate
whether there is a linear trend in the data.

Correlations
The process of variable selection for the model was an iterative one,
beginning with an initial exploration of inter-variable relationships.
Following our strategy of categorizing variables into three overarching
groups—Internal, Spatial, and Demographic—we proceeded to evaluate and
visualize the correlations between these variables and the
sale_price. Broadly speaking, we observed that internal and
demographic variables exhibited the most pronounced correlations with
sale_price, with only a limited number of spatial variables
demonstrating noteworthy associations with the independent variables
sale_price.
Correlation Matrix: Internal Variables
For this step, a list of important internal variables for real
estate, like sale price, property size, and various features, is defined
in the ‘internalvariables’ list. The code then processes the data,
picking out only the numbers from these internal variables and removing
any rows with missing data. After that, it calculates how these internal
variables are related to each other using a correlation matrix.
Upon analyzing the results, we observe that variables such as price
per square foot, number of rooms, and total livable area exhibit strong
correlations with the sale price. However, it’s essential to keep in
mind that the price per square foot is derived from the sale price,
which explains the high correlation. Similarly, for the number of rooms
and number of bedrooms, considering the imputations made earlier in this
analysis, it becomes a questionable choice to include them in the final
regression analysis due to the high number of missing values and the
general uncertainty surrounding the assigned values, especially given
the high correlation with another variable,
total_living_area, which is already included in the
analysis.
The final internal variables selected for this analysis include:
total_livable_area,number_of_bathrooms,
fireplaces, exterior_condition, and
central_air.
internalvariables <- c(
"sale_price", "PricePerSqft", "total_livable_area", "number_of_rooms", "number_of_bathrooms", "number_of_bedrooms", "BuildingAge", "frontage", "depth", "fireplaces", "BasementPresent", "GaragePresent", "exterior_condition", "number_stories", "central_air"
)
numericintVars <- data %>%
st_drop_geometry(data) %>%
select(internalvariables)%>%
na.omit()
ggcorrplot(
round(cor(numericintVars), 1),
p.mat = cor_pmat(numericintVars),
colors = c('#d7191c','#ffffbf','#2c7bb6'),
type="lower",
insig = "blank") +
labs(title = "Correlation across Internal Variables")

Correlation Matrix: Spatial Variables
Unexpectedly, during our analysis, we discovered that there were
relatively few strong positive or negative correlations among the
spatial elements we examined. Unlike the demographics and internal
variables, which demonstrated significant influence on the sale price,
these spatial factors seemed to have less impact. Despite our best
efforts and attempts to be innovative in our variable selection, the
overall correlations appeared to be less pronounced in this context.
This observation might also be linked to the absence of error clustering
that we identified later in our analysis. If time was no contraint, we
would have explored other spatial varibles further, hoping to find more
statistically significant ones.
spatvariables <- c("sale_price", "dist_to_nearest_school", "dist_to_nearest_pvt_school", "dist_to_nearest_landmark", "dist_to_comm_corr", "dist_to_nearest_station", "dist_to_nearest_trail", "dist_to_nearest_police_station","crime_nn5", "litter", "within_com_corr", "within_flood", "dist_to_floodplain", "dist_to_nearest_tenniscourt", "dist_to_nearest_playground", "dist_to_nearest_pool"
)
numericspatVars <- data %>%
st_drop_geometry(data) %>%
select(all_of(spatvariables))%>%
na.omit()
ggcorrplot(
round(cor(numericspatVars), 1),
p.mat = cor_pmat(numericspatVars),
colors = c('#d7191c','#ffffbf','#2c7bb6'),
type="lower",
insig = "blank") +
labs(title = "Correlation across Spatial Variables")

Correlation Matrix: Demographic Variables
In this section of the code, we compile a list of significant
demographic variables, including factors such as racial composition,
educational attainment, income levels, housing, and more. Upon analyzing
the results, the generated correlation plot illustrates how these
demographic factors interrelate.
In our analysis, we observe a nearly pure negative correlation
between ‘percent white’ and ‘percent black.’ To mitigate
multicollinearity, we recommend including only one of these variables in
the final model. ‘Median income’ displays a robust and highly positive
correlation with ‘sale price,’ making it a valuable addition to the
model, given its strong predictive power. Similarly, ‘monthly cost,’
which represents the cost of utilities per month in houses with a
mortgage, exhibits a noteworthy positive correlation with ‘sale price,’
making it another worthy inclusion in the final model, as it promises to
enhance our predictive capabilities.
demvariables <- c(
"sale_price", "PctWhite", "PctBlack", "PctHispanic", "PctBachelors", "PctPoverty", "med_income",
"med_rent", "owner_units", "renter_units"
, "same_house_75"
, "same_house_1"
, "monthly_costs_no_mortgage"
, "monthly_costs_with_mortgage"
, "med_gross_rent"
, "housing_units_with_heat"
, "edu_bachelors"
, "pop_below_poverty"
, "housing_units_high_speed_internet"
, "housing_units_no_vehicle"
)
numericdemVars <- data %>%
st_drop_geometry(data) %>%
select(demvariables)%>%
na.omit()
ggcorrplot(
round(cor(numericdemVars), 1),
p.mat = cor_pmat(numericdemVars),
colors = c('#d7191c','#ffffbf','#2c7bb6'),
type="lower",
insig = "blank") +
labs(title = "Correlation across Demographic Variables")

Final Variable Selection
Based on our initial correlation analyses, we went through an
iterative process to determine the final set of metrics for our model.
The code initially included some variables we considered, but to
minimize errors like MAPE and MAE while maximizing accuracy, we made
some substitutions. We also aimed to enhance the model’s
generalizability by carefully choosing and rejecting specific variables.
Finally, we settled on the variables presented. This comprehensive
approach allowed us to tailor our model’s variable selection to not only
reduce errors but also ensure the model’s applicability to a wide range
of scenarios, ultimately contributing to more robust and dependable
results in our regression analysis.
#variables <- c("sale_price", "number_of_bathrooms","dist_to_nearest_pvt_school", "dist_to_comm_corr", "dist_to_nearest_station", "dist_to_nearest_trail", "med_income", "dist_to_nearest_pool", "frontage", "central_air", "fireplaces", "BasementPresent", "exterior_condition", "PctWhite", "PctBlack", "total_livable_area", "BuildingAge", "PctBachelors", "PctPoverty", "PricePerSqft","total_livable_area", "housing_units_with_heat", "PctHispanic", "housing_units_high_speed_nternet")
variables <- c ("sale_price" ,"total_livable_area","number_of_bathrooms", "fireplaces", "exterior_condition", "central_air", "PctWhite", "med_income", "monthly_costs_with_mortgage" , "PctPoverty", "dist_to_floodplain", "within_com_corr")
#dropping crime for now check later
numericVars <- data %>%
st_drop_geometry(data) %>%
select(variables)%>%
na.omit()
ggcorrplot(
round(cor(numericVars), 1),
p.mat = cor_pmat(numericVars),
colors = c('#d7191c','#ffffbf','#2c7bb6'),
type="lower",
insig = "blank") +
labs(title = "Correlation across numeric variables")

OLS Regresssion Model
This code utilizes an OLS regression model to make accurate
predictions about home prices. The advantage of an OLS model is its
simplicity and ease of interpretation, making it a valuable tool for
understanding linear relationships between variables.
Testing and Training Data
First we split the modelling data into training and testing sets,
ensuring that the data is split randomly each time.
## Creating the training and test set
modelling_data <- data %>% filter(toPredict == "MODELLING")
set.seed(999) #makes sure data is split same way every time
## Splitting the data
split <- sample.split(modelling_data$objectid, SplitRatio = 0.75)
## Creating the training and test sets
train_data <- modelling_data[split,]
test_data <- modelling_data[!split,]
Regression Analysis
After running the regression analysis, we were able to come to some
interesting relations between the sale price and different variables
used in the analysis.
The coefficients for ‘total_livable_area,’ ‘number_of_bathrooms,’
‘fireplaces,’ ‘exterior_condition,’ ‘central_air,’ ‘PctWhite,’
‘med_income,’ ‘monthly_costs_with_mortgage,’ and ‘within_com_corr’ are
all statistically significant.
Total Livable Area (total_livable_area): The coefficient of
approximately 182.44 suggests that, for each additional unit of livable
area, the ‘sale_price’ is expected to increase by around 182.44 units,
provided that all other variables remain constant. This indicates a
strong positive relationship between the size of the property and its
sale price.
Number of Bathrooms (number_of_bathrooms): With a coefficient of
approximately 58659.04, each additional bathroom is associated with a
substantial increase in ‘sale_price.’ This implies that properties with
more bathrooms tend to command higher prices.
Exterior Condition (exterior_condition): The coefficient of
around -21749.80 signifies that, as the condition of the exterior
deteriorates, ‘sale_price’ tends to decrease. This negative coefficient
suggests that a well-maintained exterior has a positive impact on the
property’s value.
Monthly Costs with Mortgage (monthly_costs_with_mortgage): The
coefficient of approximately 141.14 suggests that an increase in utility
costs per month on houses with a mortgage leads to a higher
‘sale_price.’ This indicates that buyers may be willing to pay more for
properties associated with higher monthly costs, possibly due to
perceived value or amenities.
Within Community Correlation (within_com_corr): With a
coefficient of roughly 35629.83, a higher within-community correlation
is associated with an increase in ‘sale_price.’ This implies that
properties in areas where housing prices are positively correlated tend
to command higher prices, reflecting the desirability of such
neighborhoods.
We also then use the regression to predict home prices for the
testing dataset, to then perform accuracy and generalizablity tests on
it to refine the regression and clean and filter the dataset. We did
this over and over, in order to choose the variables that made the most
accurate and generalizable model.
#regression on training data
reg2 <- lm(sale_price ~ ., data = st_drop_geometry(train_data) %>%
dplyr::select(variables))
summary(reg2)
##
## Call:
## lm(formula = sale_price ~ ., data = st_drop_geometry(train_data) %>%
## dplyr::select(variables))
##
## Residuals:
## Min 1Q Median 3Q Max
## -925027 -53706 -880 47764 4303943
##
## Coefficients:
## Estimate Std. Error t value
## (Intercept) -225746.85879 10883.67836 -20.742
## total_livable_area 182.43776 2.64092 69.081
## number_of_bathrooms 58659.04249 3269.79875 17.940
## fireplaces 37136.07889 4427.86873 8.387
## exterior_condition -21749.80113 1520.52545 -14.304
## central_air 27556.24176 2915.03121 9.453
## PctWhite 615.15749 57.79495 10.644
## med_income 0.46934 0.07668 6.120
## monthly_costs_with_mortgage 141.14049 3.47093 40.664
## PctPoverty 39.37502 135.93348 0.290
## dist_to_floodplain -2.33719 1.46705 -1.593
## within_com_corr 35629.83413 4668.07114 7.633
## Pr(>|t|)
## (Intercept) < 0.0000000000000002 ***
## total_livable_area < 0.0000000000000002 ***
## number_of_bathrooms < 0.0000000000000002 ***
## fireplaces < 0.0000000000000002 ***
## exterior_condition < 0.0000000000000002 ***
## central_air < 0.0000000000000002 ***
## PctWhite < 0.0000000000000002 ***
## med_income 0.0000000009595338 ***
## monthly_costs_with_mortgage < 0.0000000000000002 ***
## PctPoverty 0.772
## dist_to_floodplain 0.111
## within_com_corr 0.0000000000000246 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 130100 on 13295 degrees of freedom
## (223 observations deleted due to missingness)
## Multiple R-squared: 0.6867, Adjusted R-squared: 0.6864
## F-statistic: 2649 on 11 and 13295 DF, p-value: < 0.00000000000000022
## Plot regression
#effect_plot(reg2, pred = number_of_bathrooms, interval = TRUE, plot.points = TRUE)
plot_summs(reg2, scale = TRUE)

#predicting price for testing data
test_data <-
test_data %>%
mutate(Price.Predict = predict(reg2, test_data),
Price.Error = Price.Predict - sale_price,
Price.AbsError = abs(Price.Predict - sale_price),
Price.APE = (abs(Price.Predict - sale_price)) / Price.Predict)
Error Analysis: Accuracy
The mean absolute error (MAE) for our model is currently
approximately $73,000, and the Mean Absolute Percentage
Error (MAPE) stands at around 28%. We experimented with various variable
combinations to reduce these metrics, and we managed to achieve results
close to $20,000 (with a MAPE of 3%) by incorporating
variables like “PricePerSqFT.” However, including these variables, which
are derived from the provided sale_price, led to an overfit model that
lacked generalizability – a crucial requirement for our application.
Additionally, to predict home prices effectively, we must avoid using
variables derived from home prices themselves, as this would create a
convoluted and self-referential model.
# table of MAE and MAPE
## Accuracy - Mean Absolute Error
MAE <- data.frame(mean(test_data$Price.AbsError, na.rm = T))
MAPE <- data.frame(mean(test_data$Price.APE, na.rm = T)) #MAPE
reg.MAE.MAPE <- cbind(MAE, MAPE) %>%
kable(format = "html", caption = "Regression MAE & MAPE (Figure 5.1)", col.names = c("Mean Absolute Error", "Mean Absolute Percentage Error")) %>%
kable_styling("striped", full_width = FALSE)
reg.MAE.MAPE
Regression MAE & MAPE (Figure 5.1)
|
Mean Absolute Error
|
Mean Absolute Percentage Error
|
|
72986.7
|
0.2861265
|
Generalizabiltiy
Using kfold we check generalizability
We used 100 folds for cross-validation. The high values of RMSE
indicate that the model is being affected by outliers significantly. To
address this issue, we conducted additional data cleaning and
implemented outlier filtering techniques to minimize errors. However,
the average RMSE value still remains above 100,000, making it relatively
inaccurate for predicting home prices.
The R-squared statistic represents the proportion of the variance in
the sale prices that the model can explain. It ranges from 0.3 to 0.9,
with a mean value of 0.7. A higher R-squared value indicates a better
fit of the model to the data. On average, our model can explain
approximately 70% of the variance in the data, indicating that it
possesses a reasonable level of generalizability.
test_data <-
test_data %>%
filter(sale_price < 10000000)
## K-Fold: Generalizability Cross-Validation
fitControl <- trainControl(method = "cv", number = 100)
set.seed(999)
reg.cv <-
train(sale_price ~ ., data = st_drop_geometry(test_data) %>%
dplyr::select(variables),
method = "lm", trControl = fitControl, na.action = na.pass)
stargazer(as.data.frame(reg.cv$resample), type="text", digits=1, title="Cross Validation Results (5.2)", out = "CV.txt") #all cv
##
## Cross Validation Results (5.2)
## ===================================================
## Statistic N Mean St. Dev. Min Max
## ---------------------------------------------------
## RMSE 100 113,131.9 66,213.0 61,449.3 583,198.8
## Rsquared 100 0.7 0.1 0.3 0.9
## MAE 100 71,080.9 15,814.0 44,469.5 155,013.9
## ---------------------------------------------------
Most of our MAE seem to be within the 60,000 dollar to 80,000 dollar
range (with a mean of 71,000) which is illustrated in the following
histogram.
#plotting the cross validation stuff
ggplot(reg.cv$resample, aes(x=MAE)) +
geom_histogram(fill = "#2c7bb6") +
labs(title = "Cross Validation Tests in Mean Average Error", caption="Figure 5.3") +
plotTheme()
The predicted and observed prices look evenly distributed above and
below the perfect prediction line, below the $50,000 line. Our model
moves below the perfect prediction line beyond 50,000 dollars indicating
that our model may be under-predicting the values of high value
homes.
test_data %>%
dplyr::select(Price.Predict, sale_price) %>%
ggplot(aes(sale_price, Price.Predict)) +
geom_point() +
stat_smooth(aes(sale_price, sale_price),
method = "lm", se = FALSE, size = 1, colour="#d7191c") +
stat_smooth(aes(Price.Predict, sale_price),
method = "lm", se = FALSE, size = 1, colour="#2c7bb6") +
labs(title="Predicted Sale Price as a Function of Observed Price",
subtitle="Red line represents a perfect prediction; Blue line represents prediction",
caption = "Figure 6.2",
x = "Observed Price",
y = "Predicted Price") +
plotTheme()+ xlim(0, 2000000) + ylim(0, 2000000)

Spatial Correlations
#use the same thng for wights and morans i
#move the filters to this cel too
# Spatial Lag for price
coords <- st_coordinates(test_data)
neighborList <- knn2nb(knearneigh(coords, 5))
spatialWeights <- nb2listw(neighborList, style="W")
test_data$lagPrice <- lag.listw(spatialWeights, test_data$sale_price)
test_data$lagPriceError <- lag.listw(spatialWeights, test_data$Price.Error, NAOK = TRUE)
# Filtering greater than 0 values
test_data_filter <- test_data %>%
filter(Price.Error > 0, lagPriceError > 0)
ggplot(data = test_data_filter, aes(lagPriceError, Price.Error)) +
geom_point(size = .85,colour = "black") +
geom_smooth(method = "lm",colour = "red",size = 1.2) +
labs(title="Price Errors",
caption = "Figure 6.1") +
plotTheme()
## Moran’s I
We observe a low Moran’s I value, indicating that errors are
dispersed across our data. This suggests that there is no significant
clustering of errors, a pattern that is also evident in the map.
Consequently, it is probable that errors in our model may stem from
other demographic or internal variables that we may not have accounted
for. To improve the accuracy of our analysis, further investigation and
inclusion of these potentially omitted variables might be necessary.
# for morans 1
# Spatial Lag for price
coords <- st_coordinates(test_data_filter)
neighborList <- knn2nb(knearneigh(coords, 5))
spatialWeights <- nb2listw(neighborList, style="W")
test_data_filter$lagPrice <- lag.listw(spatialWeights, test_data_filter$sale_price)
test_data_filter$lagPriceError <- lag.listw(spatialWeights, test_data_filter$Price.Error, NAOK = TRUE)
moranTest <- moran.mc(na.omit(test_data_filter$Price.Error),
spatialWeights, nsim = 999)
ggplot(as.data.frame(moranTest$res[c(1:999)]), aes(moranTest$res[c(1:999)])) +
geom_histogram(binwidth = 0.005) +
geom_vline(aes(xintercept = moranTest$statistic), colour = "#d7191c",size=1) +
scale_x_continuous(limits = c(-0.5,0.5)) +
labs(title="Observed and permuted Moran's I",
subtitle= "Observed Moran's I in red",
caption = "Figure 6.3",
x="Moran's I",
y="Count") +
plotTheme()

ggplot() +
geom_sf(data = tracts21, fill = "grey89", color = "grey89") +
geom_sf(data = test_data, aes(colour = q5(Price.AbsError)),
show.legend = "point", size = .75) +
scale_colour_manual(values = palettee,
labels=qBr(test_data,"Price.AbsError"),
name="Quintile\nBreaks") +
labs(title="Map of Price Absolute Errors for Test Set",
subtitle= "Philadelphia",
caption="Figure 6.4") +
mapTheme()

ggplot() +
geom_sf(data = tracts21, fill = "grey89", color = "grey89") +
geom_sf(data = test_data, aes(colour = q5(Price.AbsError)),
show.legend = "point", size = .75) +
scale_colour_manual(values = palettee,
labels=qBr(test_data,"Price.Predict"),
name="Quintile\nBreaks") +
labs(title="Predicted Sale Price for Test Set",
subtitle = "Philadelphia",
caption="Figure 6.4") +
mapTheme()
# Differing Contexts
Here we try to observe trends of error and predictions within each
census tract. We can see that the MAPE lies within a 0 to 1 range, and
is higher in certain census tracts in North Philadelphia, indicating a
potential missing spatial factor.
test_data <-
test_data %>%
group_by(census_tract) %>%
mutate(MeanPrice = mean(sale_price))
test_data <-
test_data %>%
group_by(census_tract) %>%
mutate(MAPE = mean(Price.APE)) %>%
filter(MAPE> 0)
ggplot(test_data) +
geom_point(aes(MeanPrice, MAPE)) +
geom_smooth(method = "lm", aes(MeanPrice, MAPE), se = FALSE, colour = "green") +
labs(title = "MAPE by Census Tract as a function of Mean Price by Census Tract",
x = "Mean Home Price", y = "MAPE",
caption = "Figure 7.5") +
plotTheme()
## Tidycensus
Discussion
Assessing the model’s effectiveness is a nuanced task. While it
exhibits moderate generalizability, the presence of notably high errors
raises concerns about its predictive accuracy. One intriguing finding
was the lack of spatial correlation with sale prices, defying the
intuitive expectation that properties near landmarks or private schools
would command higher prices. We were able to predict approximately 70%
of the variance in sale prices, which is a reasonable level of
explanatory power for a regression model.
Understanding the errors in predictions is complicated by their
dispersed nature and the absence of clustering. The low Moran’s I
statistic indicates a lack of spatial autocorrelation, making it
challenging to attribute errors to specific geographic patterns. The
influence of demographic factors, as outlined earlier, may be
contributing to the observed errors.
When it comes to spatial variation, the dispersed errors make it
difficult to identify specific areas where the model performed
exceptionally well or poorly. However, it’s worth noting that there is a
higher error count in South and Center City, suggesting the possibility
of unaccounted spatial factors in those regions.
Prediction Challenge
All those things considered we used the model to predict home prices
for the challenge data set. We notice that the number has gone down from
100 to 79, but we know that this is as a consequence of our data
cleaning and removing data points with missing or incomplete values that
could not be imputed (such as year built).
#for prediction
reg1 <- lm(sale_price ~ ., data = st_drop_geometry(data_og) %>%
dplyr::filter(toPredict == "MODELLING") %>%
dplyr::select(variables))
data_pred <-
data_og %>%
mutate(Price.Predict = predict(reg1,data_og))
final_pred <- st_drop_geometry(data_pred)%>%
filter(toPredict== "CHALLENGE")%>%
select(musaID,Price.Predict)%>%
mutate(team = "Sam and Roshini")
write.csv(final_pred, "./outputs/skhare_rganesh_challenge.csv")
#map
ggplot() +
geom_sf(data = tracts21, fill = "grey89", color = "grey89") +
geom_sf(data = data_pred, aes(colour = q5(Price.Predict)),
show.legend = "point", size = .75) +
scale_colour_manual(values = palettee,
labels=qBr(data_pred,"Price.Predict"),
name="Quintile\nBreaks") +
labs(title="Predicted Sale Price for all Data",
subtitle = "Philadelphia") +
mapTheme()

Conclusion
We would recommend this model to Zillow if the errors weren’t so
high. The model performs well in many contexts but if time was no bound,
we would have really liked to explore more variables and refine it more
to minimize the errors before sharing the model.
LS0tDQp0aXRsZTogIlByZWRpY3RpbmcgSG91c2luZyBQcmljZXMgaW4gUGhpbGFkZWxwaGlhLCBQQSINCmF1dGhvcjogIlNhbXJpZGRoaSBLaGFyZSwgUm9zaGluaSBHYW5lc2giDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiBqb3VybmFsICANCi0tLQ0KDQojIEludHJvZHVjdGlvbg0KDQpUaGUgUGhpbGFkZWxwaGlhIGhvdXNpbmcgbWFya2V0IGlzIGEgZHluYW1pYyBhbmQgZGl2ZXJzZSBsYW5kc2NhcGUgaW5mbHVlbmNlZCBieSB2YXJpb3VzIGZhY3RvcnMgdGhhdCBpbXBhY3QgYm90aCBwcmljZXMgYW5kIG92ZXJhbGwgbWFya2V0IGNvbmRpdGlvbnMuIFRoaXMgbWFya2V0IHJlZmxlY3RzIHRoZSBlY29ub21pYyBhbmQgc29jaWFsIGR5bmFtaWNzIG9mIHRoZSBjaXR5IGFuZCB0aGUgc3Vycm91bmRpbmcgcmVnaW9uLkl0IGVuY29tcGFzc2VzIGEgd2lkZSByYW5nZSBvZiBuZWlnaGJvcmhvb2RzLCBlYWNoIHdpdGggaXRzIHVuaXF1ZSBjaGFyYWN0ZXIsIGFtZW5pdGllcywgYW5kIGhvdXNpbmcgc3R5bGVzLiBUaGUgY2l0eSdzIGRpdmVyc2UgaG91c2luZyBzdG9jayBpbmNsdWRlcyByb3dob3VzZXMsIHRvd25ob21lcywgYXBhcnRtZW50cywgYW5kIHNpbmdsZS1mYW1pbHkgaG9tZXMsIGNhdGVyaW5nIHRvIGEgdmFyaWV0eSBvZiBsaWZlc3R5bGVzIGFuZCBwcmVmZXJlbmNlcy4NCg0KPGltZyBzcmM9Ii4vaW1hZ2VzLzMzcmQtU3RyZWV0LUNvcm5lci1vZi1XZXN0LUxlaGlnaC1BdmUuanBnIiB3aWR0aD0iMzAwIiBhbGlnbj0icmlnaHQiIHN0eWxlPSJkaXNwbGF5OiBpbmxpbmU7IG1hcmdpbjogMCAxMHB4OyIvPg0KDQpTaWduaWZpY2FuY2Ugb2YgcHJvamVjdA0KDQpIb3VzaW5nIHByaWNlcyBpbiBQaGlsYWRlbHBoaWEgaGF2ZSBleHBlcmllbmNlZCBmbHVjdHVhdGlvbnMgb3ZlciB0aGUgeWVhcnMsIGluZmx1ZW5jZWQgYnkgYSBjb21iaW5hdGlvbiBvZiBsb2NhbCBhbmQgbmF0aW9uYWwgZmFjdG9ycy4gSG93ZXZlciwgaXQncyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IHRoZXNlIHByaWNlcyBjYW4gdmFyeSBzaWduaWZpY2FudGx5IGJhc2VkIG9uIGZhY3RvcnMgc3VjaCBhcyB0aGUgbmVpZ2hib3Job29kLCBwcm9wZXJ0eSB0eXBlLCBhbmQgY29uZGl0aW9uIG9mIHRoZSBob21lLiANCg0KU2FsZSBwcmVkaWN0aW9uIGFsZ29yaXRobXMgYXJlIHBvd2VyZnVsIHRvb2xzIHRoYXQgb2ZmZXIgYmVuZWZpdHMgdG8gYSB3aWRlIHJhbmdlIG9mIHN0YWtlaG9sZGVycyBpbiB0aGUgaG91c2luZyBtYXJrZXQuIFRoZXkgcHJvbW90ZSBpbmZvcm1lZCBkZWNpc2lvbi1tYWtpbmcsIHVuY292ZXIgaW5lcXVhbGl0aWVzLCBhbmQgY29udHJpYnV0ZSB0byB0aGUgb3ZlcmFsbCBoZWFsdGggYW5kIGZhaXJuZXNzIG9mIHRoZSByZWFsIGVzdGF0ZSBtYXJrZXQuDQoNClRoaXMgcHJvamVjdCBidWlsZHMgb24gdGhpcyB0aGVvcnkgYW5kIGFpbXMgdG8gZXZhbHVhdGUgdGhlIHZhcmlvdXMgZmFjdG9ycyBhZmZlY3RpbmcgaG91c2luZyBwcmljZXMgdG8gY3JlYXRlIGFuIGFjY3VyYXRlIGFuZCBnZW5lcmFsaXphYmxlIE9MUyByZWdyZXNzaW9uIG1vZGVsIHRvIHByZWRpY3QgdGhlIGhvdXNpbmcgcHJpY2VzIGluIFBoaWxhZGVscGhpYS4NCg0KS2V5IGNoYWxsZW5nZXMNCg0KQnVpbGRpbmcgYSBwcmVkaWN0aXZlIG1vZGVsIGZvciBwcm9wZXJ0eSB2YWx1YXRpb24gaXMgZGlmZmljdWx0IGdpdmVuIHRoZSBpbnRyaWNhY2llcyBpbnZvbHZlZCBpbiBkYXRhIHByb2Nlc3NpbmcgYW5kIGludGVycHJldGF0aW9uLiAgSXQgaW52b2x2ZXMgc29ydGluZyB0aHJvdWdoIGxvdHMgb2YgZGF0YSwgY2hvb3NpbmcgdGhlIHJpZ2h0IGZlYXR1cmVzLCB1bmRlcnN0YW5kaW5nIHRoZSBudWFuY2VkIGluZmx1ZW5jZSBvZiBuZWlnaGJvcmhvb2QgY2hhcmFjdGVyaXN0aWNzLCBhZGRyZXNzaW5nIGNvbGxpbmVhcml0eSBhbW9uZyB2YXJpYWJsZXMsIHNlbGVjdGluZyBhcHByb3ByaWF0ZSBtb2RlbGluZyB0ZWNobmlxdWVzLCBldmFsdWF0aW5nIG1vZGVsIHBlcmZvcm1hbmNlLCBlbnN1cmluZyBpbnRlcnByZXRhYmlsaXR5LCBhbmQgYnVpbGRpbmcgYSBnZW5lcmFsaXphYmxlIG1vZGVsLiANCg0KIyMgUHJvY2Vzcw0KDQpUaGUgcHJvY2VzcyBvZiBjcmVhdGluZyB0aGlzIG1vZGVsIHdhcyBleHRyZW1lbHkgaXRlcmF0aXZlLCBhbmQgaW52b2x2ZWQgbXVsdGlwbGUgcm91bmRzIG9mIHRyaWFsIGFuZCBlcnJvciwgcGFydGljdWxhcmx5IGluIGF0dGVtcHRzIHRvIG1pbmltaXplIHRoZSBlcnJvcnMgaW4gcHJlZGljdGlvbnMuIFRoZSBtYXBzLCBjb3JyZWxhdGlvbiBtYXRyaXggYW5kIHNjYXR0ZXJwbG90cyBhcmUgYWxsIGlsbHVzdHJhdGl2ZSBvZiB2YXJpYWJsZXMgdGhhdCB3ZXJlIGNvbnNpZGVyZWQsIHdyYW5nbGVkIGFuZCBmaWx0ZXJlZCBhcyBwYXJ0IG9mIHRoZSBkYXRhIGFuYWx5c2lzIHByb2Nlc3MuICANCg0KIyMgU3VtbWFyeSBvZiBSZXN1bHRzDQoNClRoZSBtb2RlbCBleGhpYml0cyBtb2RlcmF0ZSBnZW5lcmFsaXphYmlsaXR5LCBob3dldmVyLCB0aGUgcHJlc2VuY2Ugb2Ygbm90YWJseSBoaWdoIGVycm9ycyByYWlzZXMgY29uY2VybnMgYWJvdXQgaXRzIHByZWRpY3RpdmUgYWNjdXJhY3kuIEluIG91ciBwcm9jZXNzIG9mIGV4cGVyaW1lbnRhdGlvbiwgd2Ugd2VyZSBhYmxlIHRvIHByZWRpY3QgYXBwcm94aW1hdGVseSA3MCUgb2YgdGhlIHZhcmlhbmNlIGluIHNhbGUgcHJpY2VzLCB3aGljaCBpcyBhIHJlYXNvbmFibGUgbGV2ZWwgb2YgZXhwbGFuYXRvcnkgcG93ZXIgZm9yIGEgcmVncmVzc2lvbiBtb2RlbC4gVGhlIGVycm9ycyBhY3Jvc3MgdGhlIG1vZGVsIHNlZW0gdG8gYmUgZGlzcGVyc2VkIGFuZCBkbyBub3QgZGlzcGxheSBhbnkgc2lnbmlmaWNhbnQgc3BhdGlhbCBhdXRvLWNvcnJlbGF0aW9uLiANCg0KVGhpcyBjb2RlIGlzIGJ1aWx0IHVwb24gdGhlIGNsYXNzd29yayBkaXNjdXNzZWQgW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9tYWZpY2htYW4vbXVzYV81MDgwXzIwMjMvdHJlZS9tYWluKS4NCg0KYGBge3IgU2V0dXAgS25pdHRpbmcgUGFyYW1ldGVycywgaW5jbHVkZT1GQUxTRX0NCiAga25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICAgIGVjaG8gPSBUUlVFLA0KICAgIHdhcm5pbmcgPSBGQUxTRSwNCiAgICBtZXNzYWdlID0gRkFMU0UsDQogICBvdXQud2lkdGggPSAnMTAwJScsDQogICAgZmlnLnJldGluYSA9Mw0KICApDQpgYGANCg0KIyMgUiBTZXR1cCBhbmQgSW5zdGFsbGluZyBQYWNrYWdlcw0KDQpUaGlzIGNvZGUgY2h1bmsgaGFuZGxlcyB0aGUgZXNzZW50aWFsIHRhc2tzIG9mIGxvYWRpbmcgbmVjZXNzYXJ5IHBhY2thZ2VzLCBjb25maWd1cmluZyB0aGUgQ2Vuc3VzIEFQSSBrZXksIGRlZmluaW5nIGNsYXNzIGZ1bmN0aW9ucywgc3BlY2lmeWluZyBhIGNvbG9yIHBhbGV0dGUsIGFuZCBtYW5hZ2luZyBnbG9iYWwgZW52aXJvbm1lbnQgc2V0dGluZ3MuDQoNCmBgYHtyIFNldCB1cCBQYWNrYWdlcywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9DQoNCiMgTG9hZGluZyBsaWJyYXJpZXMNCg0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHRpZHljZW5zdXMpDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkodmlyaWRpcykNCmxpYnJhcnkoc3RyaW5ncikNCmxpYnJhcnkodGlncmlzKQ0KbGlicmFyeShnZ2NvcnJwbG90KQ0KbGlicmFyeShzdGFyZ2F6ZXIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShjYVRvb2xzKQ0KbGlicmFyeShzcGRlcCkNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KGp0b29scykgICAgIA0KbGlicmFyeShnZ3N0YW5jZSkgDQpsaWJyYXJ5KGdncHVicikgICAgDQpsaWJyYXJ5KGJyb29tLm1peGVkKSANCg0KIyBTZXR0aW5nIHBhcmFtZXRlcnMgZm9yIHNjaWVudGlmaWMgbm90YXRpb24NCg0Kb3B0aW9ucyhzY2lwZW49OTk5KQ0Kb3B0aW9ucyh0aWdyaXNfY2xhc3MgPSAic2YiKQ0KDQojIEZ1bmN0aW9ucyBhbmQgZGF0YSBkaXJlY3RvcnkNCg0Kc291cmNlKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vdXJiYW5TcGF0aWFsL1B1YmxpYy1Qb2xpY3ktQW5hbHl0aWNzLUxhbmRpbmcvbWFzdGVyL2Z1bmN0aW9ucy5yIikNCg0KIyBJbnZva2luZyBjb2xvciBwYWxldHRlcyB0byBiZSB1c2VkDQoNCnBhbGV0dGVlIDwtIGMoJyNkNzE5MWMnLCcjZmRhZTYxJywnI2ZmZmZiZicsJyNhYmQ5ZTknLCcjMmM3YmI2JykNCg0KIyBSZWdpc3RlcmluZyBBUEkgS2V5IHRvIGJlIHVzZWQNCg0KY2Vuc3VzX2FwaV9rZXkoJ2JmMmQ1MDc2NTFiNWE2MjFkYmFkZDQ0NTMzZmI0ZjNkZWFhYjI2YmYnLCBvdmVyd3JpdGUgPSBUUlVFKQ0KDQpgYGANCg0KIyBEYXRhIFdyYW5nbGluZw0KDQpDZW5zdXMsIE9wZW5EYXRhIFBoaWxseSBhbmQgcHJvdmlkZWQgRGF0YXNldCANCg0KIyMgUHJvdmlkZWQgRGF0YXNldCBhbmQgSW50ZXJuYWwgVmFyaWFibGVzIA0KDQpUaGUgY29kZSBiZWxvdyBpcyB1c2VkIHRvIGltcG9ydCB0aGUgZm91bmRhdGlvbmFsIGRhdGFzZXQgZm9yIG91ciBtb2RlbC4gVGhpcyBkYXRhc2V0IGNvbnRhaW5zIGluZm9ybWF0aW9uIG9uIGhvbWUgc2FsZXMgcHJpY2VzIGFuZCBwcm9wZXJ0eSBjaGFyYWN0ZXJpc3RpY3MgaW4gUGhpbGFkZWxwaGlhLCBQZW5uc3lsdmFuaWEgZm9yIHRoZSB5ZWFycyAyMDIyIGFuZCAyMDIzLiBUaGUgbW9kZWwgd2UgYXJlIGRldmVsb3BpbmcgaXMgZGVzaWduZWQgdG8gbWFrZSBwcmVkaWN0aW9ucyBhYm91dCBob21lIHByaWNlcyBhbmQgdXRpbGl6ZXMgc3BlY2lmaWMgcHJvcGVydHkgYXR0cmlidXRlcyBmcm9tIHRoaXMgZGF0YXNldCB0byBlbmhhbmNlIHRoZSBhY2N1cmFjeSBvZiB0aG9zZSBwcmVkaWN0aW9ucy4NCg0KVGhlIGNvZGUgZGVtb25zdHJhdGVzIGRhdGEgbWFuaXB1bGF0aW9uIHVzaW5nIHRoZSBkcGx5ciBwYWNrYWdlIHRvIHJlZmluZSB0aGUgZGF0YXNldCBieSBzZWxlY3Rpbmcgc3BlY2lmaWMgY29sdW1ucyBhbmQgZmlsdGVyaW5nIHJvd3MgYmFzZWQgb24gY3JpdGVyaWEuIFRoZXNlIG9wZXJhdGlvbnMgYXJlIGNhcnJpZWQgb3V0IGJlY2F1c2UgdGhlIHJhdyBkYXRhc2V0IGlzIHVudGlkeSBhbmQgaW5jb21wbGV0ZS4gVGhlIG9yaWdpbmFsIGRhdGFzZXQgaXMgZnVydGhlciBtb2RpZmllZCBieSBmaWx0ZXJpbmcgcm93cyBzcGVjaWZpYyB0byBQaGlsYWRlbHBoaWEgQ2l0eSBhbmQgYnkgZXhjbHVkaW5nIGNlcnRhaW4gY29sdW1ucyB3aXRoIGEgbWFqb3JpdHkgb2YgbWlzc2luZyB2YWx1ZXMuDQoNCmBgYHtyIFJlYWRpbmcgRGF0YSwgcmVzdWx0cz0gJ2hpZGUnfQ0KDQojIFJlYWRpbmcgRGF0YQ0KDQpkYXRhIDwtIA0KICBzdF9yZWFkKCIuL2RhdGEvc3R1ZGVudERhdGEuZ2VvanNvbiIpICU+JQ0KICBzdF90cmFuc2Zvcm0oJ0VTUkk6MTAyMjg2JykNCg0KIyBEcm9wcGluZyBjb2x1bW5zIHdpdGggbm8gdmFsdWVzIGFuZCBmaWx0ZXJpbmcgdmFsdWVzIHdpdGhpbiBQaGlsYWRlbHBoaWENCg0KZGF0YSA8LSAgZGF0YSAlPiUgDQogIHNlbGVjdCgtY3Jvc3NfcmVmZXJlbmNlLCAtZGF0ZV9leHRlcmlvcl9jb25kaXRpb24sIC1tYWlsaW5nX2FkZHJlc3NfMiwgLW1haWxpbmdfY2FyZV9vZiwgLXVuZmluaXNoZWQsIC11dGlsaXR5LCAtY2F0ZWdvcnlfY29kZSwgLWNhdGVnb3J5X2NvZGVfZGVzY3JpcHRpb24sIC1leGVtcHRfbGFuZCwgLXNlcGFyYXRlX3V0aWxpdGllcywgLXNld2VyLCAtIHNpdGVfdHlwZSwgLWhvdXNlX2V4dGVuc2lvbiwgLXN0cmVldF9kaXJlY3Rpb24sIC1zdWZmaXgsIC1nYXJhZ2VfdHlwZSwgLWdlbmVyYWxfY29uc3RydWN0aW9uICklPiUgDQogIGZpbHRlcihtYWlsaW5nX2NpdHlfc3RhdGUgPT0gIlBISUxBREVMUEhJQSBQQSIgKQ0KDQpgYGANCiMjIyBDYXRlZ29yaWNhbCBWYXJpYWJsZXMgDQoNClRoZSBuZXh0IHN0ZXAgaXMgcHJlZG9taW5hbnRseSBmb2N1c2VkIG9uIGRhdGEgY2xlYW5pbmcgYW5kIHRyYW5zZm9ybWF0aW9uIGJhc2VkIG9uIHRoZSBpbmZvcm1hdGlvbiBnaXZlbiBpbiB0aGUgbWV0YWRhdGEgZm9yIHRoZSBkYXRhc2V0IGJ5IHRoZSBDaXR5IG9mIFBoaWxhZGVscGhpYS4gT3ZlcmFsbCwgdGhpcyBjb2RlIGFpbXMgdG8gY2xlYW4gYW5kIGNhdGVnb3JpemUgdmFyaW91cyBjb2x1bW5zIGluIHRoZSBkYXRhc2V0LiBJdCBkb2VzIHRoaXMgYnkgaGFuZGxpbmcgbWlzc2luZyB2YWx1ZXMgYXMgd2VsbCBhcyBhc3NpZ25pbmcgY2F0ZWdvcmllcyBhbmQgYmluYXJ5IHZhbHVlcyBmb3IgYmV0dGVyIGFuYWx5c2lzIGFuZCBtb2RlbGluZy4NCg0KDQpgYGB7ciBDbGVhbmluZyBEYXRhIGJhc2VkIG9uIE1ldGFkYXRhLCByZXN1bHRzPSAnaGlkZSd9DQoNCiMjIEZpbHRlcmluZyBvdXQgOSByb3dzIHdoZXJlIHllYXIgYnVpbHQgaXMgbm90IGdpdmVuDQoNCmRhdGEgPC0gIGRhdGEgJT4lIA0KICBmaWx0ZXIoeWVhcl9idWlsdCA+IDAgKQ0KDQojIyBDYXRlZ29yaXppbmcgaWYgYSBCYXNlbWVudCBpcyBwcmVzZW50DQoNCmRhdGEgPC0gZGF0YSAlPiUNCiAgbXV0YXRlKEJhc2VtZW50UHJlc2VudCA9IGNhc2Vfd2hlbihiYXNlbWVudHMgPT0gJ0EnIHwNCiAgYmFzZW1lbnRzID09ICdCJyB8DQogIGJhc2VtZW50cyA9PSAnQycgfA0KICBiYXNlbWVudHMgPT0gJ0QnIHwNCiAgYmFzZW1lbnRzID09ICdFJyB8DQogIGJhc2VtZW50cyA9PSAnRicgfA0KICBiYXNlbWVudHMgPT0gJ0cnIHwNCiAgYmFzZW1lbnRzID09ICdIJyB8DQogIGJhc2VtZW50cyA9PSAnSScgfA0KICBiYXNlbWVudHMgPT0gJ0onIH4gMSwNCiAgYmFzZW1lbnRzID09ICcwJyB+IDApKQ0KDQojIEFzc2lnbmluZyB2YWx1ZSBvZiAwIHRvICdOQScgcm93cyBiYXNlZCBvbiBkZXNjcmlwdGlvbiBpbiBtZXRhZGF0YQ0KDQpkYXRhJEJhc2VtZW50UHJlc2VudFtpcy5uYShkYXRhJEJhc2VtZW50UHJlc2VudCldIDwtIDANCg0KIyMgQ2F0ZWdvcml6aW5nIEJhc2VtZW50IFR5cGUNCg0KbGlicmFyeShkcGx5cikNCg0KZGF0YSA8LSBkYXRhICU+JQ0KICBtdXRhdGUoQmFzZW1lbnRUeXBlID0gY2FzZV93aGVuKGJhc2VtZW50cyA9PSAnQScgfiAnRnVsbCBzaXplIGZpbmlzaGVkJywNCiAgYmFzZW1lbnRzID09ICdCJyB+ICdGdWxsIHNpemUgc2VtaS1maW5pc2hlZCcsDQogIGJhc2VtZW50cyA9PSAnQycgfiAnRnVsbCBzaXplIHVuZmluaXNoZWQnLA0KICBiYXNlbWVudHMgPT0gJ0QnIH4gJ0Z1bGwgc2l6ZSB1bmtub3duIGZpbmlzaCcsDQogIGJhc2VtZW50cyA9PSAnRScgfiAnUGFydGlhbCBzaXplIGZpbmlzaGVkJywNCiAgYmFzZW1lbnRzID09ICdGJyB+ICdQYXJ0aWFsIHNpemUgc2VtaS1maW5pc2hlZCcsDQogIGJhc2VtZW50cyA9PSAnRycgfiAnUGFydGlhbCBzaXplIHVuZmluaXNoZWQnLA0KICBiYXNlbWVudHMgPT0gJ0gnIH4gJ1BhcnRpYWwgc2l6ZSB1bmtub3duIGZpbmlzaCcsDQogIGJhc2VtZW50cyA9PSAnSScgfiAnVW5rbm93biBzaXplIGZpbmlzaGVkJywNCiAgYmFzZW1lbnRzID09ICdKJyB+ICdVbmtub3duIHNpemUgdW5maW5pc2hlZCcsDQogIGJhc2VtZW50cyA9PSAnMCcgfiAnTm8gYmFzZW1lbnQnKSkNCg0KIyBBc3NpZ25pbmcgdmFsdWUgb2YgMCB0byAnTkEnIHJvd3MgYmFzZWQgb24gZGVzY3JpcHRpb24gaW4gbWV0YWRhdGENCg0KZGF0YSRCYXNlbWVudFR5cGVbaXMubmEoZGF0YSRCYXNlbWVudFR5cGUpXSA8LSAiTm8gYmFzZW1lbnQiDQpkYXRhJGJhc2VtZW50c1tpcy5uYShkYXRhJGJhc2VtZW50cyldIDwtIDANCg0KIyMgQ2F0ZWdvcml6aW5nIGJhc2VkIG9uIENlbnRyYWwgQWlyDQoNCmRhdGEkY2VudHJhbF9haXIgPC0gaWZlbHNlKGRhdGEkY2VudHJhbF9haXIgPT0gJ1knLCAxLCAwKQ0KDQojIyBDYXRlZ29yaXppbmcgYmFzZWQgb24gRXh0ZXJpb3IgQ29uZGl0aW9uDQoNCmRhdGEgPC0gZGF0YSAlPiUNCiAgbXV0YXRlKEV4dGVyaW9yQ29uZGl0aW9uVHlwZSA9IGNhc2Vfd2hlbihleHRlcmlvcl9jb25kaXRpb24gPT0gJzEnIHwNCiAgZXh0ZXJpb3JfY29uZGl0aW9uID09ICcyJyB8DQogIGV4dGVyaW9yX2NvbmRpdGlvbiA9PSAnMycgfiAnR29vZCBDb25kaXRpb24nLA0KICBleHRlcmlvcl9jb25kaXRpb24gPT0gJzQnIHwNCiAgZXh0ZXJpb3JfY29uZGl0aW9uID09ICc1JyB+ICdBdmVyYWdlIENvbmRpdGlvbicsDQogIGV4dGVyaW9yX2NvbmRpdGlvbiA9PSAnNicgfiAnQmVsb3cgQXZlcmFnZSBDb25kaXRpb24nLA0KICBleHRlcmlvcl9jb25kaXRpb24gPT0gJzcnIH4gJ1ZhY2FudCBhbmQgU2VhbGVkJykpDQoNCiMgQXNzaWduaW5nIHZhbHVlIHRvIHNpbmdsZSAnTkEnIHJvdyBiYXNlZCBvbiAnaW50ZXJpb3JfY29uc3RydWN0aW9uJyByYXRpbmcgZm9yIHNhbWUgcm93DQoNCmRhdGEkRXh0ZXJpb3JDb25kaXRpb25UeXBlW2lzLm5hKGRhdGEkRXh0ZXJpb3JDb25kaXRpb25UeXBlKV0gPC0gIkdvb2QgQ29uZGl0aW9uIg0KZGF0YSRleHRlcmlvcl9jb25kaXRpb25baXMubmEoZGF0YSRleHRlcmlvcl9jb25kaXRpb24pXSA8LSAzDQoNCiMjIENhdGVnb3JpemluZyBiYXNlZCBvbiBGaXJlcGxhY2VzDQoNCiMgQXNzaWduaW5nIHZhbHVlIG9mIDAgdG8gJ05BJyByb3dzIGJhc2VkIG9uIGRlc2NyaXB0aW9uIGluIG1ldGFkYXRhDQoNCmRhdGEkZmlyZXBsYWNlc1tpcy5uYShkYXRhJGZpcmVwbGFjZXMpXSA8LSAwDQoNCiMjIENhdGVnb3JpemluZyBiYXNlZCBvbiBGdWVsIFNvdXJjZXMNCg0KZGF0YSA8LSBkYXRhICU+JQ0KICBtdXRhdGUoRnVlbFR5cGUgPSBjYXNlX3doZW4oZnVlbCA9PSAnQScgfiAnTmF0dXJhbCBHYXMgUG93ZXJlZCcsDQogIGZ1ZWwgPT0gJ0InIH4gJ09pbCBQb3dlcmVkJywNCiAgZnVlbCA9PSAnQycgfiAnRWxlY3RyaWMgUG93ZXJlZCcsDQogIGZ1ZWwgPT0gJ0UnIH4gJ1NvbGFyIFBvd2VyZWQnLA0KICBmdWVsID09ICdHJyB+ICdGdWVsIFNvdXJjZSBVbmtub3duJykpDQoNCiMgQXNzaWduaW5nIHZhbHVlIG9mIFVua25vd24vRyB0byAnTkEnIHJvd3MgYmFzZWQgb24gZGVzY3JpcHRpb24gaW4gbWV0YWRhdGENCg0KZGF0YSRGdWVsVHlwZVtpcy5uYShkYXRhJEZ1ZWxUeXBlKV0gPC0gIkZ1ZWwgU291cmNlIFVua25vd24iDQpkYXRhJGZ1ZWxbaXMubmEoZGF0YSRmdWVsKV0gPC0gIkciDQoNCiMjIENhdGVnb3JpemluZyBiYXNlZCBvbiBHYXJhZ2UgUHJlc2VuY2UNCg0KZGF0YSA8LSBkYXRhICU+JQ0KICBtdXRhdGUoR2FyYWdlUHJlc2VudCA9IGNhc2Vfd2hlbihnYXJhZ2Vfc3BhY2VzID09ICcwJyB+IDAsDQogIGdhcmFnZV9zcGFjZXMgPT0gJzEnIHwNCiAgZ2FyYWdlX3NwYWNlcyA9PSAnMicgfA0KICBnYXJhZ2Vfc3BhY2VzID09ICczJyB+IDEpKQ0KDQojIEFzc2lnbmluZyB2YWx1ZSBvZiAwIHRvICdOQScgcm93cyBiYXNlZCBvbiBkZXNjcmlwdGlvbiBpbiBtZXRhZGF0YQ0KDQpkYXRhJEdhcmFnZVByZXNlbnRbaXMubmEoZGF0YSRHYXJhZ2VQcmVzZW50KV0gPC0gMA0KZGF0YSRnYXJhZ2Vfc3BhY2VzW2lzLm5hKGRhdGEkZ2FyYWdlX3NwYWNlcyldIDwtIDANCg0KIyMgQ2F0ZWdvcml6aW5nIGJhc2VkIG9uIEdhcmFnZSBUeXBlcw0KDQpkYXRhIDwtIGRhdGEgJT4lDQogIG11dGF0ZShHYXJhZ2VUeXBlID0gY2FzZV93aGVuKGdhcmFnZV9zcGFjZXMgPT0gJzAnIH4gJ05vIEdhcmFnZScsDQogIGdhcmFnZV9zcGFjZXMgPT0gJzEnIH4gJ1NpbmdsZSBHYXJhZ2UnLA0KICBnYXJhZ2Vfc3BhY2VzID09ICcyJyB8DQogIGdhcmFnZV9zcGFjZXMgPT0gJzMnIH4gJ011bHRpcGxlIEdhcmFnZXMnKSkNCg0KIyBBc3NpZ25pbmcgdmFsdWUgb2YgMCB0byAnTkEnIHJvd3MgYmFzZWQgb24gZGVzY3JpcHRpb24gaW4gbWV0YWRhdGENCg0KZGF0YSRHYXJhZ2VUeXBlW2lzLm5hKGRhdGEkR2FyYWdlVHlwZSldIDwtICJObyBHYXJhZ2UiDQpkYXRhJGdhcmFnZV9zcGFjZXNbaXMubmEoZGF0YSRnYXJhZ2Vfc3BhY2VzKV0gPC0gMA0KDQoNCmRhdGEgPC0gZGF0YSAlPiUNCiAgbXV0YXRlKEludGVyaW9yQ29uZGl0aW9uVHlwZSA9IGNhc2Vfd2hlbiggaW50ZXJpb3JfY29uZGl0aW9uID09ICcwJyB8DQogIGludGVyaW9yX2NvbmRpdGlvbiA9PSAnMScgfA0KICBpbnRlcmlvcl9jb25kaXRpb24gPT0gJzInIHwNCiAgaW50ZXJpb3JfY29uZGl0aW9uID09ICczJyB+ICdHb29kIENvbmRpdGlvbicsDQogIGludGVyaW9yX2NvbmRpdGlvbiA9PSAnNCcgfiAnQXZlcmFnZSBDb25kaXRpb24nLA0KICBpbnRlcmlvcl9jb25kaXRpb24gPT0gJzUnIH4gJ0JlbG93IEF2ZXJhZ2UgQ29uZGl0aW9uJywNCiAgaW50ZXJpb3JfY29uZGl0aW9uID09ICc2JyB8DQogIGludGVyaW9yX2NvbmRpdGlvbiA9PSAnNycgfiAnVmFjYW50IGFuZC9vciBTZWFsZWQnKSkNCg0KIyBBc3NpZ25pbmcgdmFsdWVzIHRvIDAgb3IgJ05BJyByb3dzIGJhc2VkIG9uIGV4dGVyaW9yIGNvbmRpdGlvbiBzaW5jZSBpbnRlcmlvciBhbmQgZXh0ZXJpb3IgY29uZGl0aW9ucyBhcmUgZXF1YWwgaW4gYWxtb3N0IGFsbCBjYXNlcw0KDQpkYXRhJEludGVyaW9yQ29uZGl0aW9uVHlwZVtpcy5uYShkYXRhJEludGVyaW9yQ29uZGl0aW9uVHlwZSldIDwtIGMoZGF0YSRFeHRlcmlvckNvbmRpdGlvblR5cGUpDQoNCiMjIENhdGVnb3JpemluZyBiYXNlZCBvbiBWaWV3IFR5cGUNCg0KZGF0YSA8LSBkYXRhICU+JQ0KICBtdXRhdGUoVmlld1R5cGUgPSBjYXNlX3doZW4odmlld190eXBlID09ICcwJyB+ICdOYXR1cmUgb2YgVmlldyBVbmtub3duJywNCiAgdmlld190eXBlID09ICdJJyB+ICdUeXBpY2FsIFZpZXcnLA0KICB2aWV3X3R5cGUgPT0gJ0EnIH4gJ1NreWxpbmUgVmlldycsDQogIHZpZXdfdHlwZSA9PSAnQicgfiAnUml2ZXIgVmlldycsDQogIHZpZXdfdHlwZSA9PSAnQycgfiAnUGFyayBWaWV3JywNCiAgdmlld190eXBlID09ICdEJyB+ICdDb21tZXJjaWFsIEFyZWEgVmlldycsDQogIHZpZXdfdHlwZSA9PSAnRScgfiAnSW5kdXN0cmlhbCBBcmVhIFZpZXcnLA0KICB2aWV3X3R5cGUgPT0gJ0gnIH4gJ1ZpZXcgb2YgTGFuZG1hcmsnKSkNCg0KIyBBc3NpZ25pbmcgdmFsdWUgdG8gTkEgcm93cyB0byBuYXR1cmUgb2YgdmlldyB1bmtub3duDQoNCmRhdGEkVmlld1R5cGVbaXMubmEoZGF0YSRWaWV3VHlwZSldIDwtICJOYXR1cmUgb2YgVmlldyBVbmtub3duIg0KZGF0YSR2aWV3X3R5cGVbaXMubmEoZGF0YSR2aWV3X3R5cGUpXSA8LSAwDQoNCiMjIENhdGVnb3JpemluZyBiYXNlZCBvbiBUb3BvZ3JhcGh5IFR5cGUNCg0KZGF0YSA8LSBkYXRhICU+JQ0KICBtdXRhdGUoVG9wb2dyYXBoeVR5cGUgPSBjYXNlX3doZW4odG9wb2dyYXBoeSA9PSAnQScgfiAnQWJvdmUgU3RyZWV0IExldmVsIFRvcG9ncmFwaHknLA0KICB0b3BvZ3JhcGh5ID09ICdCJyB+ICdCZWxvdyBTdHJlZXQgTGV2ZWwgVG9wb2dyYXBoeScsDQogIHRvcG9ncmFwaHkgPT0gJ0MnIH4gJ0Zsb29kIFBsYWluIFRvcG9ncmFwaHknLA0KICB0b3BvZ3JhcGh5ID09ICdEJyB+ICdSb2NreSBUb3BvZ3JhcGh5JywNCiAgdG9wb2dyYXBoeSA9PSAnRScgfiAnT3RoZXIgVG9wb2dyYXBoeScsDQogIHRvcG9ncmFwaHkgPT0gJ0YnIH4gJ0xldmVsIFRvcG9ncmFwaHknKSkNCg0KIyBBc3NpZ25pbmcgdmFsdWUgdG8gTkEgcm93cyB0byBuYXR1cmUgb2YgdmlldyB1bmtub3duDQoNCmRhdGEkVG9wb2dyYXBoeVR5cGVbaXMubmEoZGF0YSRUb3BvZ3JhcGh5VHlwZSldIDwtICJUb3BvZ3JhcGh5IFVua25vd24iDQpkYXRhJHRvcG9ncmFwaHlbaXMubmEoZGF0YSR0b3BvZ3JhcGh5KV0gPC0gMA0KDQojIyBDYXRlZ29yaXppbmcgYmFzZWQgb24gUGFyY2VsIFR5cGUNCg0KZGF0YSA8LSBkYXRhICU+JQ0KICBtdXRhdGUoUGFyY2VsVHlwZSA9IGNhc2Vfd2hlbihwYXJjZWxfc2hhcGUgPT0gJ0EnIH4gJ0lycmVndWxhciBQYXJjZWwnLA0KICBwYXJjZWxfc2hhcGUgPT0gJ0InIH4gJ0dyb3NzbHkgSXJyZWd1bGFyIFBhcmNlbCcsDQogIHBhcmNlbF9zaGFwZSA9PSAnQycgfiAnVHJpYW5ndWxhciBQYXJjZWwnLA0KICBwYXJjZWxfc2hhcGUgPT0gJ0QnIH4gJ0xvbmcgTmFycm93IFBhcmNlbCcsDQogIHBhcmNlbF9zaGFwZSA9PSAnRScgfiAnUmVjdGFuZ3VsYXIgUGFyY2VsJykpDQoNCiMgQXNzaWduaW5nIHZhbHVlIHRvIE5BIHJvd3MgdG8gbmF0dXJlIG9mIHZpZXcgdW5rbm93bg0KDQpkYXRhJFBhcmNlbFR5cGVbaXMubmEoZGF0YSRQYXJjZWxUeXBlKV0gPC0gIlBhcmNlbCBUeXBlIFVua25vd24iDQpkYXRhJHBhcmNlbF9zaGFwZVtpcy5uYShkYXRhJHBhcmNlbF9zaGFwZSldIDwtIDANCg0KDQpgYGANCg0KIyMjIEltcHV0aW5nIE1pc3NpbmcgRGF0YSANCg0KQW4gaW5pdGlhbCBhbmFseXNpcyBvZiB0aGUgZGF0YXNldCBpbmRpY2F0ZWQgbWlzc2luZyB2YWx1ZXMgZm9yIGEgbnVtYmVyIG9mIHJvd3Mgd2l0aGluIGltcG9ydGFudCBmYWN0b3JzIHRoYXQgaW5mb3JtIHNhbGUgcHJpY2VzIHN1Y2ggYXMgbnVtYmVyIG9mIGJlZHJvb21zLCBudW1iZXIgb2YgYmF0aHJvb21zLCBhbmQgbnVtYmVyIG9mIHJvb21zLiBBIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIGlzIHVzZWQgdG8gcHJlZGljdCB0aGVzZSBtaXNzaW5nIHZhbHVlcyBiYXNlZCBvbiB0aGUgcmVsYXRpb25zaGlwcyBvYnNlcnZlZCBpbiB0aGUgYXZhaWxhYmxlIGRhdGEuIA0KDQpJbiB0aGUgZ2l2ZW4gZGF0YXNldCwgYSBzdHJvbmcgcmVsYXRpb24gaXMgb2JzZXJ2ZWQgYmV0d2VlbiBudW1iZXIgb2Ygcm9vbXMgYW5kIHRvdGFsIGxpdmFibGUgYXJlYS4gSGVyZSwgdGhlIGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGVzZSB0d28gZmFjdG9ycyBhcmUgc3R1ZGllZCBhY3Jvc3MgdGhlIGRhdGFzZXQgYW5kIHByb2plY3RlZCB0byB0aGUgcm93cyB3aXRoIHRoZSBtaXNzaW5nIHZhbHVlcy4gVGhlIGFzc3VtcHRpb24gaGVyZSBpcyB0aGF0LCB3aGVuIHBsb3R0ZWQgb24gYSBncmFwaCwgdGhlIG1pc3NpbmcgdmFsdWVzIHdpbGwgaGF2ZSB0aGUgc2FtZSBzbG9wZSBhbmQgaW50ZXJjZXB0IGFzIHRoZSByZXN0IG9mIHRoZSBkYXRhc2V0LiANCg0KVGhpcyBpbXB1dGluZyBwcmFjdGljZSBpcyBleHRlbmRlZCB0byBvdGhlciBmaWVsZHMgd2l0aCBtaXNzaW5nIHZhbHVlcyBzdWNoIGFzIG51bWJlciBvZiBiYXRocm9vbXMgYW5kIG51bWJlciBvZiBiZWRyb29tcy4NCg0KIyMjIyBCZWRyb29tcw0KDQpgYGB7ciBJbXB1dGluZyB2YWx1ZXMgZm9yIG1pc3NpbmcgdmFsdWVzIG9mIG51bWJlciBvZiBiZWRyb29tcywgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9DQoNCiMjIEltcHV0aW5nIHZhbHVlcyBmb3IgbWlzc2luZyB2YWx1ZXMgb2YgbnVtYmVyIG9mIGJlZHJvb21zIGJhc2VkIG9uIHRvdGFsIGxpdmFibGUgYXJlYQ0KDQojIERyb3BwaW5nIDMyIHZhbHVlcyBvZiB0b3RhbCBsaXZhYmxlIGFyZWEgd2hpY2ggYXJlIDAgZm9yIGJldHRlciBwcmVkaWN0aW9uDQoNCmRhdGEgPC0gIGRhdGEgJT4lIA0KICBmaWx0ZXIodG90YWxfbGl2YWJsZV9hcmVhID4gMCApDQoNCiMgU3RlcCAxIC0gQ3JlYXRpbmcgYW4gaW5kZXggb2YgMCBhbmQgMSB2YWx1ZXMgZm9yIHJvd3MgdGhhdCBoYXZlIHZhbHVlcyBmb3IgbnVtYmVyIG9mIGJhdGhyb29tcyBhbmQgcm93cyB0aGF0IGRvIG5vdA0KDQpkYXRhIDwtIGRhdGEgJT4lDQogIG11dGF0ZShCZWRyb29tSW5kZXggPSBjYXNlX3doZW4obnVtYmVyX29mX2JlZHJvb21zID49IDEgfiAxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bWJlcl9vZl9iZWRyb29tcyA8IDEgfiAwKSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCmRhdGEkQmVkcm9vbUluZGV4W2lzLm5hKGRhdGEkQmVkcm9vbUluZGV4KV0gPC0gMA0KDQojIFN0ZXAgMiAtIENyZWF0aW5nIGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgcmVsYXRpbmcgbnVtYmVyIG9mIGJlZHJvb21zIGFuZCB0b3RhbCBsaXZlYWJsZSBhcmVhDQoNCmxtKG51bWJlcl9vZl9iZWRyb29tcyB+IHRvdGFsX2xpdmFibGVfYXJlYSwgZGF0YT1kYXRhKQ0KDQojIFN0ZXAgMyAtIEltcHV0aW5nIG5ldyB2YWx1ZXMgZm9yIG1pc3NpbmcgdmFsdWVzIG9mIG51bWJlciBvZiBiZWRyb29tcyB1c2luZyByZWdyZXNzaW9uIHJlc3VsdHMNCg0KZm9yKGkgaW4gMTpucm93KGRhdGEpKQ0Kew0KICBpZiAoZGF0YSRCZWRyb29tSW5kZXhbaV0gPT0gMCkNCiAgew0KICAgIGRhdGEkbnVtYmVyX29mX2JlZHJvb21zW2ldID0gMi4xNjA1NjUwICsgMC4wMDAzMDU3KmRhdGEkdG90YWxfbGl2YWJsZV9hcmVhW2ldDQogIH0NCiAgfQ0KDQpkYXRhJG51bWJlcl9vZl9iZWRyb29tcyA8LSByb3VuZChkYXRhJG51bWJlcl9vZl9iZWRyb29tcywgZGlnaXRzPTApDQoNCmBgYA0KDQojIyMjIFJvb21zIA0KDQpgYGB7ciBJbXB1dGluZyB2YWx1ZXMgZm9yIG1pc3NpbmcgdmFsdWVzIG9mIG51bWJlciBvZiByb29tcywgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9DQoNCiMjIEltcHV0aW5nIHZhbHVlcyBmb3IgbWlzc2luZyB2YWx1ZXMgb2YgbnVtYmVyIG9mIHJvb21zIGJhc2VkIG9uIHRvdGFsIGxpdmFibGUgYXJlYQ0KDQojIFN0ZXAgMSAtIENyZWF0aW5nIGFuIGluZGV4IG9mIDAgYW5kIDEgdmFsdWVzIGZvciByb3dzIHRoYXQgaGF2ZSB2YWx1ZXMgZm9yIG51bWJlciBvZiByb29tcyBhbmQgcm93cyB0aGF0IGRvIG5vdA0KDQpkYXRhIDwtIGRhdGEgJT4lDQogIG11dGF0ZShSb29tSW5kZXggPSBjYXNlX3doZW4obnVtYmVyX29mX3Jvb21zID49IDEgfiAxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bWJlcl9vZl9yb29tcyA8IDEgfiAwKSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCmRhdGEkUm9vbUluZGV4W2lzLm5hKGRhdGEkUm9vbUluZGV4KV0gPC0gMA0KDQojIFN0ZXAgMiAtIENyZWF0aW5nIGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgcmVsYXRpbmcgbnVtYmVyIG9mIHJvb21zIGFuZCB0b3RhbCBsaXZhYmxlIGFyZWENCg0KbG0obnVtYmVyX29mX3Jvb21zIH4gdG90YWxfbGl2YWJsZV9hcmVhLCBkYXRhPWRhdGEpDQoNCiMgU3RlcCAzIC0gSW1wdXRpbmcgbmV3IHZhbHVlcyBmb3IgbWlzc2luZyB2YWx1ZXMgb2YgbnVtYmVyIG9mIHJvb21zIHVzaW5nIHJlZ3Jlc3Npb24gcmVzdWx0cw0KDQpmb3IoaSBpbiAxOm5yb3coZGF0YSkpDQp7DQogIGlmIChkYXRhJFJvb21JbmRleFtpXSA9PSAwKQ0KICB7DQogICAgZGF0YSRudW1iZXJfb2Zfcm9vbXNbaV0gPSA0LjMxNjUwMyArIDAuMDAxMzE5KmRhdGEkdG90YWxfbGl2YWJsZV9hcmVhW2ldDQogIH0NCiAgfQ0KDQpkYXRhJG51bWJlcl9vZl9yb29tcyA8LSByb3VuZChkYXRhJG51bWJlcl9vZl9yb29tcywgZGlnaXRzPTApDQoNCmBgYA0KDQojIyMjIEJhdGhyb29tcw0KDQpgYGB7ciBJbXB1dGluZyB2YWx1ZXMgZm9yIG1pc3NpbmcgdmFsdWVzIG9mIG51bWJlciBvZiBiYXRocm9vbXMsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KDQojIyBJbXB1dGluZyB2YWx1ZXMgZm9yIG1pc3NpbmcgdmFsdWVzIG9mIG51bWJlciBvZiBiYXRocm9vbXMgYmFzZWQgb24gdG90YWwgbGl2YWJsZSBhcmVhDQoNCiMgU3RlcCAxIC0gQ3JlYXRpbmcgYW4gaW5kZXggb2YgMCBhbmQgMSB2YWx1ZXMgZm9yIHJvd3MgdGhhdCBoYXZlIHZhbHVlcyBmb3IgbnVtYmVyIG9mIGJhdGhyb29tcyBhbmQgcm93cyB0aGF0IGRvIG5vdA0KDQpkYXRhIDwtIGRhdGEgJT4lDQogIG11dGF0ZShCYXRocm9vbUluZGV4ID0gY2FzZV93aGVuKG51bWJlcl9vZl9iYXRocm9vbXMgPj0gMSB+IDEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtYmVyX29mX2JhdGhyb29tcyA8IDEgfiAwKSkNCg0KZGF0YSRCYXRocm9vbUluZGV4W2lzLm5hKGRhdGEkQmF0aHJvb21JbmRleCldIDwtIDANCg0KIyBTdGVwIDIgLSBDcmVhdGluZyBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHJlbGF0aW5nIG51bWJlciBvZiBiYXRocm9vbXMgYW5kICB0b3RhbCBsaXZhYmxlIGFyZWENCg0KbG0obnVtYmVyX29mX2JhdGhyb29tcyB+IHRvdGFsX2xpdmFibGVfYXJlYSwgZGF0YT1kYXRhKQ0KDQojIFN0ZXAgMyAtIEltcHV0aW5nIG5ldyB2YWx1ZXMgZm9yIG1pc3NpbmcgdmFsdWVzIG9mIG51bWJlciBvZiBiYXRocm9vbXMgdXNpbmcgcmVncmVzc2lvbiByZXN1bHRzDQoNCmZvcihpIGluIDE6bnJvdyhkYXRhKSkNCnsNCiAgaWYgKGRhdGEkQmF0aHJvb21JbmRleFtpXSA9PSAwKQ0KICB7DQogICAgZGF0YSRudW1iZXJfb2ZfYmF0aHJvb21zW2ldID0gMC42NDI2MzEwICsgMC4wMDAzMjgzKmRhdGEkdG90YWxfbGl2YWJsZV9hcmVhW2ldDQogIH0NCiAgfQ0KDQpkYXRhJG51bWJlcl9vZl9iYXRocm9vbXMgPC0gcm91bmQoZGF0YSRudW1iZXJfb2ZfYmF0aHJvb21zLCBkaWdpdHM9MCkNCg0KYGBgDQoNCiMjIyBDcmVhdGluZyBOZXcgVmFyaWFibGVzIA0KDQpQb3N0IGNsZWFuaW5nLCB0d28gbmV3IHZhcmlhYmxlcyBhcmUgY3JlYXRlZCB3aXRoaW4gdGhlIGRhdGFzZXQgdG8gZXZhbHVhdGUgdGhlIHByaWNlIHBlciBzcXVhcmUgZm9vdCB3aXRoaW4gc29sZCBwcm9wZXJ0aWVzIGFzIHdlbGwgYXMgdGhlIGFnZSBvZiB0aGUgc29sZCBwcm9wZXJ0aWVzLiANCg0KYGBge3IgUHJpY2UvU3FmdGEsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KDQpkYXRhIDwtIA0KICBkYXRhICU+JQ0KICBtdXRhdGUoUHJpY2VQZXJTcWZ0ID0gKHNhbGVfcHJpY2UvdG90YWxfbGl2YWJsZV9hcmVhKSkNCg0KZGF0YSA8LSANCiAgZGF0YSAlPiUNCiAgbXV0YXRlKEJ1aWxkaW5nQWdlID0gKDIwMjMgLSAoeWVhcl9idWlsdCkpKQ0KDQpgYGANCg0KIyMjIERyb3BwaW5nIENvbHVtbnMgYW5kIE91dGxpZXJzIA0KDQpBcyB0aGUgbGFzdCBzdGVwLCB0aGUgZm91bmRhdGlvbmFsIGRhdGFzZXQgY2FycnlpbmcgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHByb3BlcnRpZXMgaXMgZmlsdGVyZWQgdG8gb25seSBob2xkIHRoZSBpbmRpY2F0aXZlIGludGVybmFsIHZhcmlhYmxlcyB3aGljaCBtYXkgcGxheSBhIHJvbGUgaW4gaW5mbHVlbmNpbmcgdGhlIGhvbWUgcHJpY2UgdmFsdWUuIFRvIGFjaGlldmUgYmV0dGVyIGFjY3VyYWN5IGFuZCBmZXdlciBlcnJvcnMgaW4gcHJlZGljdGlvbiwgb3V0bGllciB2YWx1ZXMgaW4gdGhlIGRhdGFzZXQgYXJlIGVsaW1pbmF0ZWQgdG8gcmVtb3ZlIGFueSBzaWduaWZpY2FudCBza2V3cyBpbiB0aGUgZGF0YXNldC4NCg0KDQpgYGB7ciBmaWx0ZXIgZGF0YSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9DQoNCg0KZGF0YSA8LSBkYXRhICU+JSANCiAgc2VsZWN0KG9iamVjdGlkLCBhc3Nlc3NtZW50X2RhdGUsIEJ1aWxkaW5nQWdlLCB5ZWFyX2J1aWx0LCBidWlsZGluZ19jb2RlLCBidWlsZGluZ19jb2RlX2Rlc2NyaXB0aW9uLCBwaW4sIGJ1aWxkaW5nX2NvZGVfbmV3LCBidWlsZGluZ19jb2RlX2Rlc2NyaXB0aW9uX25ldywgIGNlbnN1c190cmFjdCwgZ2VvZ3JhcGhpY193YXJkLCB6b25pbmcsIGxvY2F0aW9uLCBzdHJlZXRfbmFtZSwgc3RyZWV0X2NvZGUsIHN0cmVldF9kZXNpZ25hdGlvbiwgemlwX2NvZGUsIGhvdXNlX251bWJlciwgZGVwdGgsIGZyb250YWdlLCBjZW50cmFsX2FpciwgZmlyZXBsYWNlcywgZnVlbCwgRnVlbFR5cGUsIGJhc2VtZW50cywgQmFzZW1lbnRQcmVzZW50LCBCYXNlbWVudFR5cGUsIGdhcmFnZV9zcGFjZXMsIEdhcmFnZVByZXNlbnQsIEdhcmFnZVR5cGUsIGV4dGVyaW9yX2NvbmRpdGlvbiwgRXh0ZXJpb3JDb25kaXRpb25UeXBlLCBpbnRlcmlvcl9jb25kaXRpb24sIEludGVyaW9yQ29uZGl0aW9uVHlwZSwgbnVtYmVyX29mX2JhdGhyb29tcywgbnVtYmVyX29mX2JlZHJvb21zLCBudW1iZXJfb2Zfcm9vbXMsIG51bWJlcl9zdG9yaWVzLCB0b3RhbF9saXZhYmxlX2FyZWEsIHZpZXdfdHlwZSwgVmlld1R5cGUsIHRvcG9ncmFwaHksIFRvcG9ncmFwaHlUeXBlLCBwYXJjZWxfc2hhcGUsIFBhcmNlbFR5cGUsIHNhbGVfZGF0ZSwgc2FsZV95ZWFyLCBzYWxlX3ByaWNlLCBQcmljZVBlclNxZnQsIG11c2FJRCwgdG9QcmVkaWN0LCBnZW9tZXRyeSApDQpgYGANCg0KYGBge3IgZmlsdGVyIG91dGxpZXJzLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCg0KZGF0YSA8LSAgZGF0YSAlPiUgDQogIGZpbHRlcihudW1iZXJfb2ZfYmVkcm9vbXMgPDEwLCBudW1iZXJfb2Zfcm9vbXM8MTUsICgobnVtYmVyX29mX2JhdGhyb29tcytudW1iZXJfb2ZfYmVkcm9vbXMpIDwgbnVtYmVyX29mX3Jvb21zKSwgUHJpY2VQZXJTcWZ0PDE1MDAsIHRvdGFsX2xpdmFibGVfYXJlYTwxMDAwMCkNCg0KYGBgDQoNCg0KIyMgQ2Vuc3VzIERhdGENCg0KQWZ0ZXIgY2xlYW5pbmcgdGhlIHByaW1hcnkgZGF0YXNldCBhbmQgaW1wb3J0aW5nIGRhdGEgb24gc3Vycm91bmRpbmcgcGh5c2ljYWwgYW1lbml0aWVzLCB0aGUgbmV4dCBzdGVwIGlzIGdhdGhlcmluZyByZWxldmFudCBjZW5zdXMgZGF0YSB0byB1bmRlcnN0YW5kIGRlbW9ncmFwaGljIGNvbmRpdGlvbnMgcGVydGFpbmluZyB0byB0aGVzZSBkYXRhIHBvaW50cy4gVGhlIHByb3ZpZGVkIFIgY29kZSBjaHVuayBmZXRjaGVzIGFuZCBwcm9jZXNzZXMgZGF0YSBmcm9tIHRoZSBBbWVyaWNhbiBDb21tdW5pdHkgU3VydmV5IChBQ1MpIGZvciBQaGlsYWRlbHBoaWEgY2Vuc3VzIHRyYWN0cyBpbiB0aGUgeWVhciAyMDIxLiBJdCByZXRyaWV2ZXMgdmFyaW91cyBkZW1vZ3JhcGhpYyBhbmQgc29jaW9lY29ub21pYyB2YXJpYWJsZXMgZm9yIHRoZXNlIHRyYWN0cyBhbmQgcGVyZm9ybXMgc29tZSBkYXRhIHRyYW5zZm9ybWF0aW9ucy4gVGhlIHllYXIgY2hvc2VuIGZvciBhbmFseXNpcyBpcyAyMDIxIHRvIGZhY3RvciByZWNvdmVyeSBmcm9tIENvdmlkIGluIG9yZGVyIHRvIG1ha2UgbW9yZSBhY2N1cmF0ZSBwcmVkaWN0aW9ucy4gDQoNCkEgbGlzdCBvZiB2YXJpYWJsZXMgaW5jbHVkaW5nIHBvcHVsYXRpb24sIGluY29tZSwgaG91c2luZy1yZWxhdGVkIGluZm9ybWF0aW9uLCBlZHVjYXRpb24sIHBvdmVydHkgbGV2ZWxzLCBhbmQgcmFjaWFsL2V0aG5pYyBkZW1vZ3JhcGhpY3MgYXJlIGdhdGhlcmVkIGFuZCB0cmFuc2Zvcm1lZCB0byBiZSBwcm9qZWN0ZWQgaW4gdGhlIEVTUkk6MTAyNzI4IGNvb3JkaW5hdGUgc3lzdGVtLiBUaGlzIGluZm9ybWF0aW9uIGlzIHN0b3JlZCBpbiBhIG5ldyBkYXRhc2V0IC0gJ3RyYWN0czIxJy4NCg0KDQpgYGB7ciBjZW5zdXMsIGNhY2hlPVRSVUUsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9ICdoaWRlJ30NCg0KYWNzX3ZhcmlhYmxlX2xpc3QuMjAyMSA8LSBsb2FkX3ZhcmlhYmxlcygyMDIxLCAjeWVhcg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYWNzNSIsICNmaXZlIHllYXIgQUNTIGVzdGltYXRlcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYWNoZSA9IFRSVUUpDQojIDIwMjEsIEENCg0KIyBSZXRyaWV2ZSBBQ1MgZGF0YSBmb3IgUGhpbGFkZWxwaGlhIHRyYWN0cyBpbiAyMDIwDQp0cmFjdHMyMSA8LSBnZXRfYWNzKA0KICBnZW9ncmFwaHkgPSAidHJhY3QiLA0KICB2YXJpYWJsZXMgPSBjKA0KICAgICJCMDEwMDNfMDAxIiwgICAjIFRvdGFsIFBvcHVsYXRpb24NCiAgICAiQjE5MDEzXzAwMSIsICAgIyBNZWRpYW4gSG91c2Vob2xkIEluY29tZQ0KICAgICJCMjUwNThfMDAxIiwgICAjIE1lZGlhbiBSZW50DQogICAgIkIyNTAwOF8wMDIiLCAgICMgT3duZXItT2NjdXBpZWQgVW5pdHMNCiAgICAiQjI1MDA4XzAwMyIsICAgIyBSZW50ZXItT2NjdXBpZWQgVW5pdHMNCiAgICAiQjA3MDAxXzAzMiIsICAgIyBTYW1lIEhvdXNlIDc1IFllYXJzIEFnbw0KICAgICJCMDcwMDFfMDE3IiwgICAjIFNhbWUgSG91c2UgMSBZZWFyIEFnbw0KICAgICJCMjUwODhfMDAzIiwgICAjIE1lZGlhbiBTZWxlY3RlZCBNb250aGx5IE93bmVyIENvc3RzIChob21lcyB3aXRob3V0IGEgbW9ydGdhZ2UpDQogICAgIkIyNTA4OF8wMDIiLCAgICMgTWVkaWFuIFNlbGVjdGVkIE1vbnRobHkgT3duZXIgQ29zdHMgKGhvbWVzIHdpdGggYSBtb3J0Z2FnZSkNCiAgICAiQjI1MDY0XzAwMSIsICAgIyBNZWRpYW4gR3Jvc3MgUmVudCAocmVudCBhbmQgdXRpbGl0aWVzKQ0KICAgICJCMjUxMTdfMDAxIiwgICAjIFBlcmNlbnRhZ2Ugb2YgSG91c2luZyBVbml0cyB3aXRoIGhlYXQNCiAgICAiQjE1MDAzXzAyMiIsICAgIyBFZHVjYXRpb25hbCBBdHRhaW5tZW50OiBCYWNoZWxvcidzIERlZ3JlZQ0KICAgICJCMTcwMDFfMDAyIiwgICAjIFBlcmNlbnRhZ2Ugb2YgUG9wdWxhdGlvbiBCZWxvdyB0aGUgUG92ZXJ0eSBMZXZlbA0KICAgICJCMjgwMDJfMDA0IiwgICAjIFBlcmNlbnRhZ2Ugb2YgSG91c2luZyBVbml0cyB3aXRoIEhpZ2gtU3BlZWQgSW50ZXJuZXQNCiAgICAiQjI1MDQ0XzAwMyIsICAgIyBQZXJjZW50YWdlIG9mIEhvdXNpbmcgVW5pdHMgd2l0aCBObyBWZWhpY2xlIEF2YWlsYWJsZQ0KICAgICJCMDIwMDFfMDAyIiwgICAjIFJhY2UgYW5kIEV0aG5pY2l0eTogV2hpdGUgQWxvbmUNCiAgICAiQjAyMDAxXzAwMyIsICAgIyBSYWNlIGFuZCBFdGhuaWNpdHk6IEJsYWNrIG9yIEFmcmljYW4gQW1lcmljYW4gQWxvbmUNCiAgICAiQjAzMDAxXzAwMyIgICAgIyBIaXNwYW5pYyBvciBMYXRpbm8gT3JpZ2luIG9mIFBvcHVsYXRpb24NCiAgKSwNCiAgeWVhciA9IDIwMjEsDQogIHN0YXRlID0gIlBBIiwNCiAgY291bnR5ID0gIlBoaWxhZGVscGhpYSIsDQogIGdlb21ldHJ5ID0gVFJVRSwNCiAgb3V0cHV0ID0gIndpZGUiDQopJT4lDQogIHNlbGVjdCgtTkFNRSwgLWVuZHNfd2l0aCgiTSIpKSAlPiUNCiAgcmVuYW1lKHRvdGFscG9wID0gQjAxMDAzXzAwMUUsICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUb3RhbCBQb3B1bGF0aW9uDQogICAgICAgICBtZWRfaW5jb21lID0gQjE5MDEzXzAwMUUsICAgICAgICAgICAgICAgICAgICAgICAgICMgTWVkaWFuIEhvdXNlaG9sZCBJbmNvbWUNCiAgICAgICAgIG1lZF9yZW50ID0gQjI1MDU4XzAwMUUsICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBNZWRpYW4gUmVudA0KICAgICAgICAgb3duZXJfdW5pdHMgPSBCMjUwMDhfMDAyRSwgICAgICAgICAgICAgICAgICAgICAgICAjIE93bmVyLU9jY3VwaWVkIFVuaXRzDQogICAgICAgICByZW50ZXJfdW5pdHMgPSBCMjUwMDhfMDAzRSwgICAgICAgICAgICAgICAgICAgICAgICMgUmVudGVyLU9jY3VwaWVkIFVuaXRzDQogICAgICAgICBzYW1lX2hvdXNlXzc1ID0gQjA3MDAxXzAzMkUsICAgICAgICAgICAgICAgICAgICAgICMgU2FtZSBIb3VzZSA3NSBZZWFycyBBZ28NCiAgICAgICAgIHNhbWVfaG91c2VfMSA9IEIwNzAwMV8wMTdFLCAgICAgICAgICAgICAgICAgICAgICAgIyBTYW1lIEhvdXNlIDEgWWVhciBBZ28NCiAgICAgICAgIG1vbnRobHlfY29zdHNfbm9fbW9ydGdhZ2UgPSBCMjUwODhfMDAzRSwgICAgICAgICAgIyBNZWRpYW4gU2VsZWN0ZWQgTW9udGhseSBPd25lciBDb3N0cyAoaG9tZXMgd2l0aG91dCBhIG1vcnRnYWdlKQ0KICAgICAgICAgbW9udGhseV9jb3N0c193aXRoX21vcnRnYWdlID0gQjI1MDg4XzAwMkUsICAgICAgICAjIE1lZGlhbiBTZWxlY3RlZCBNb250aGx5IE93bmVyIENvc3RzIChob21lcyB3aXRoIGEgbW9ydGdhZ2UpDQogICAgICAgICBtZWRfZ3Jvc3NfcmVudCA9IEIyNTA2NF8wMDFFLCAgICAgICAgICAgICAgICAgICAgICMgTWVkaWFuIEdyb3NzIFJlbnQgKHJlbnQgYW5kIHV0aWxpdGllcykNCiAgICAgICAgIGhvdXNpbmdfdW5pdHNfd2l0aF9oZWF0ID0gQjI1MTE3XzAwMUUsICAgICAgICAgICAgIyBQZXJjZW50YWdlIG9mIEhvdXNpbmcgVW5pdHMgd2l0aCBoZWF0DQogICAgICAgICBlZHVfYmFjaGVsb3JzID0gQjE1MDAzXzAyMkUsICAgICAgICAgICAgICAgICAgICAgICMgRWR1Y2F0aW9uYWwgQXR0YWlubWVudDogQmFjaGVsb3IncyBEZWdyZWUNCiAgICAgICAgIHBvcF9iZWxvd19wb3ZlcnR5ID0gQjE3MDAxXzAwMkUsICAgICAgICAgICAgICAgICAgIyBQZXJjZW50YWdlIG9mIFBvcHVsYXRpb24gQmVsb3cgdGhlIFBvdmVydHkgTGV2ZWwNCiAgICAgICAgIGhvdXNpbmdfdW5pdHNfaGlnaF9zcGVlZF9pbnRlcm5ldCA9IEIyODAwMl8wMDRFLCAgIyBQZXJjZW50YWdlIG9mIEhvdXNpbmcgVW5pdHMgd2l0aCBIaWdoLVNwZWVkIEludGVybmV0DQogICAgICAgICBob3VzaW5nX3VuaXRzX25vX3ZlaGljbGUgPSBCMjUwNDRfMDAzRSwgICAgICAgICAgICMgUGVyY2VudGFnZSBvZiBIb3VzaW5nIFVuaXRzIHdpdGggTm8gVmVoaWNsZSBBdmFpbGFibGUNCiAgICAgICAgIHJhY2Vfd2hpdGUgPSBCMDIwMDFfMDAyRSwgICAgICAgICAgICAgICAgICAgICAgICAgIyBSYWNlIGFuZCBFdGhuaWNpdHk6IFdoaXRlIEFsb25lDQogICAgICAgICByYWNlX2JsYWNrID0gQjAyMDAxXzAwM0UsICAgICAgICAgICAgICAgICAgICAgICAgICMgUmFjZSBhbmQgRXRobmljaXR5OiBCbGFjayBvciBBZnJpY2FuIEFtZXJpY2FuIEFsb25lDQogICAgICAgICBoaXNwYW5pY19sYXRpbm8gPSBCMDMwMDFfMDAzRSAgICAgICAgICAgICAgICAgICAgICMgUmFjZSBhbmQgRXRobmljaXR5OiBIaXNwYW5pYyBvciBMYXRpbm8NCiAgICAgICAgICkNCg0KIyBUcmFuc2Zvcm0gdGhlIGRhdGEgdG8gRVNSSToxMDI3MjggcHJvamVjdGlvbg0KdHJhY3RzMjEgPC0gdHJhY3RzMjEgJT4lIHN0X3RyYW5zZm9ybShzdF9jcnMoZGF0YSkpDQoNCmBgYA0KDQojIyBPcGVuIERhdGEgcGhpbGx5IA0KDQpUbyBiZWdpbiBjb25zdHJ1Y3RpbmcgYSBwcmVkaWN0aXZlIG1vZGVsIGZvciB0aGUgUGhpbGFkZWxwaGlhIGhvdXNpbmcgbWFya2V0LCBhZGRpdGlvbmFsIGV4dGVybmFsIHZhcmlhYmxlcyBhcmUgaW5jb3Jwb3JhdGVkLiBUaGVzZSB2YXJpYWJsZXMgZXhhbWluZSB0aGUgcHJveGltaXR5IG9mIHZhcmlvdXMgYW1lbml0aWVzIGFuZCBwdWJsaWMgc2VydmljZXMgdG8gdGhlIGhvbWVzIGxpc3RlZCBpbiB0aGUgZm91bmRhdGlvbmFsIGRhdGFzZXQuIFRoZSBmb2xsb3dpbmcgY29kZSBpbXBvcnRzIGRhdGEgcmVsYXRlZCB0byB0aGVzZSBwb3RlbnRpYWxseSBpbmZsdWVudGlhbCBhbWVuaXRpZXMsIGluY2x1ZGluZyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgbG9jYXRpb25zIG9mIHB1YmxpYyBhbmQgcHJpdmF0ZSBzY2hvb2xzLCBjb2xsZWdlcywgY2l0eSBsYW5kbWFya3MsIHBsYXlncm91bmRzLCB0cmFpbHMsIHRlbm5pcyBjb3VydHMsIHBvb2xzLCB0cmFuc2l0IHN0b3BzIGFuZCBwb2xpY2Ugc3RhdGlvbnMuIEFkZGl0aW9uYWxseSwgaW5mb3JtYXRpb24gb24gcG90ZW50aWFsIGhvbWUgdmFsdWUgaW5oaWJpdG9ycyBzdWNoIGFzIGxpdHRlciwgcHJveGltaXR5IHRvIHRoZSBmbG9vZHBsYWluIGFuZCBpbnN0YW5jZXMgb2YgZ3VuIHZpb2xlbmNlIGFyZSBhbHNvIGJyb3VnaHQgaW4uIFRoZXNlIGRhdGFzZXRzIGFyZSBzb3VyY2VkIGZyb20gW09wZW4gRGF0YSBQaGlsbHkuXShodHRwczovL2RhdGEtcGhsLm9wZW5kYXRhLmFyY2dpcy5jb20vKQ0KDQpgYGB7ciBBZGRpbmcgRGF0YSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0gJ2hpZGUnfQ0KDQojIE5lYXJlc3QgU2Nob29scw0KIyMgQWRkaW5nIGRhdGEgb24gUGhpbGFkZWxwaGlhIFNjaG9vbHMNCg0KUGhpbGx5U2Nob29scyA8LQ0KICAgc3RfcmVhZCgiLi9kYXRhL1NjaG9vbHMuZ2VvanNvbiIpICU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKHRyYWN0czIxKSkNCg0KUGhpbGx5UHZ0U2Nob29scyA8LQ0KICAgc3RfcmVhZCgiLi9kYXRhL1NjaG9vbHMuZ2VvanNvbiIpICU+JQ0KICBmaWx0ZXIoVFlQRV9TUEVDSUZJQyA9PSAiUFJJVkFURSIpICU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKHRyYWN0czIxKSkNCg0KIyMgTWFwcGluZyBuZWFyZXN0IHNjaG9vbA0KDQpuZWFyZXN0X3NjaG9vbCA8LSBzZjo6c3RfbmVhcmVzdF9mZWF0dXJlKGRhdGEsIFBoaWxseVNjaG9vbHMpDQpuZWFyZXN0X3B2dF9zY2hvb2wgPC0gc2Y6OnN0X25lYXJlc3RfZmVhdHVyZShkYXRhLCBQaGlsbHlQdnRTY2hvb2xzKQ0KDQojIyBDb252ZXJ0aW5nIHNjaG9vbHMgdG8gcnNnZW8gZ2VvbWV0cmllcw0KDQp4IDwtIHJzZ2VvOjphc19yc2dlbyhkYXRhKQ0KeSA8LSByc2dlbzo6YXNfcnNnZW8oUGhpbGx5U2Nob29scykNCg0KIyMgQ2FsY3VsYXRpbmcgZGlzdGFuY2UNCg0KZGF0YSRkaXN0X3RvX25lYXJlc3Rfc2Nob29sIDwtIHJzZ2VvOjpkaXN0YW5jZV9ldWNsaWRlYW5fcGFpcndpc2UoeCwgeVtuZWFyZXN0X3NjaG9vbF0pDQoNCiMjIENvbnZlcnRpbmcgcHJpdmF0ZSBzY2hvb2xzIHRvIHJzZ2VvIGdlb21ldHJpZXMNCg0KeCA8LSByc2dlbzo6YXNfcnNnZW8oZGF0YSkNCnkgPC0gcnNnZW86OmFzX3JzZ2VvKFBoaWxseVB2dFNjaG9vbHMpDQoNCiMjIENhbGN1bGF0aW5nIGRpc3RhbmNlDQoNCmRhdGEkZGlzdF90b19uZWFyZXN0X3B2dF9zY2hvb2wgPC0gcnNnZW86OmRpc3RhbmNlX2V1Y2xpZGVhbl9wYWlyd2lzZSh4LCB5W25lYXJlc3RfcHZ0X3NjaG9vbF0pDQoNCiMgTmVhcmVzdCBDb2xsZWdlcw0KIyMgQWRkaW5nIGRhdGEgb24gUGhpbGFkZWxwaGlhIENvbGxlZ2VzDQoNClBoaWxseUNvbGxlZ2VzIDwtDQogc3RfcmVhZCgiLi9kYXRhL1VuaXZlcnNpdGllc19Db2xsZWdlcy5nZW9qc29uIiklPiUNCiAgc3RfdHJhbnNmb3JtKHN0X2Nycyh0cmFjdHMyMSkpDQoNCiMjIE1hcHBpbmcgbmVhcmVzdCBjb2xsZWdlDQoNCm5lYXJlc3RfY29sbGVnZSA8LSBzZjo6c3RfbmVhcmVzdF9mZWF0dXJlKGRhdGEsIFBoaWxseUNvbGxlZ2VzKQ0KDQojIyBDb252ZXJ0aW5nIHRvIHJzZ2VvIGdlb21ldHJpZXMNCg0KeCA8LSByc2dlbzo6YXNfcnNnZW8oZGF0YSkNCnkgPC0gcnNnZW86OmFzX3JzZ2VvKFBoaWxseUNvbGxlZ2VzKQ0KDQojIyBDYWxjdWxhdGluZyBkaXN0YW5jZQ0KDQpkYXRhJGRpc3RfdG9fbmVhcmVzdF9jb2xsZWdlIDwtIHJzZ2VvOjpkaXN0YW5jZV9ldWNsaWRlYW5fcGFpcndpc2UoeCwgeVtuZWFyZXN0X2NvbGxlZ2VdKQ0KDQojIE5lYXJlc3QgTGFuZG1hcmtzDQojIyBBZGRpbmcgZGF0YSBvbiBQaGlsYWRlbHBoaWEgbGFuZG1hcmtzIA0KDQpQaGlsbHlMYW5kbWFya3MgPC0NCiBzdF9yZWFkKCJodHRwczovL3NlcnZpY2VzLmFyY2dpcy5jb20vZkxlR2piN3U0dVhxZUY5cS9hcmNnaXMvcmVzdC9zZXJ2aWNlcy9MYW5kbWFya19Qb2ludHMvRmVhdHVyZVNlcnZlci8wL3F1ZXJ5P291dEZpZWxkcz0qJndoZXJlPTElM0QxJmY9Z2VvanNvbiIpJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnModHJhY3RzMjEpKQ0KDQojIyBNYXBwaW5nIG5lYXJlc3QgbGFuZG1hcmsNCg0KbmVhcmVzdF9sYW5kbWFyayA8LSBzZjo6c3RfbmVhcmVzdF9mZWF0dXJlKGRhdGEsIFBoaWxseUxhbmRtYXJrcykNCg0KIyMgQ29udmVydGluZyB0byByc2dlbyBnZW9tZXRyaWVzDQoNCnggPC0gcnNnZW86OmFzX3JzZ2VvKGRhdGEpDQp5IDwtIHJzZ2VvOjphc19yc2dlbyhQaGlsbHlMYW5kbWFya3MpDQoNCiMjIENhbGN1bGF0aW5nIGRpc3RhbmNlDQoNCmRhdGEkZGlzdF90b19uZWFyZXN0X2xhbmRtYXJrIDwtIHJzZ2VvOjpkaXN0YW5jZV9ldWNsaWRlYW5fcGFpcndpc2UoeCwgeVtuZWFyZXN0X2xhbmRtYXJrXSkNCg0KIyBOZWFyZXN0IENvbW1lcmNpYWwgQ29ycmlkb3JzDQojIyBBZGRpbmcgZGF0YSBvbiBQaGlsYWRlbHBoaWEgQ29tbWVyY2lhbCBDb3JyaWRvcnMgDQoNClBoaWxseUNvbUNvcnIgPC0NCiAgc3RfcmVhZCgiLi9kYXRhL0NvbW1lcmNpYWxfQ29ycmlkb3JzLmdlb2pzb24iKSAlPiUNCiAgc3RfdHJhbnNmb3JtKHN0X2Nycyh0cmFjdHMyMSkpDQoNCiMjIElzIGl0IHdpdGhpbiB0aGUgY29tbWVyY2lhbCBjb3JyaWRvcj8NCg0KZGF0YSR3aXRoaW5fY29tX2NvcnIgPC0gaWZlbHNlKHN0X3dpdGhpbihkYXRhLCBQaGlsbHlDb21Db3JyKSwgMSwgMCkNCg0KZGF0YSA8LSBkYXRhICU+JQ0KICBtdXRhdGUod2l0aGluX2NvbV9jb3JyID0gaWZlbHNlKGlzLm5hKHdpdGhpbl9jb21fY29yciksIDAsIDEpKQ0KDQojIyBNYXBwaW5nIG5lYXJlc3QgY29tbWVyY2lhbCBjb3JyaWRvcg0KDQpuZWFyZXN0X2NvcnJpZG9yIDwtIHNmOjpzdF9uZWFyZXN0X2ZlYXR1cmUoZGF0YSwgUGhpbGx5Q29tQ29ycikNCg0KIyMgQ29udmVydGluZyB0byByc2dlbyBnZW9tZXRyaWVzDQoNCnggPC0gcnNnZW86OmFzX3JzZ2VvKGRhdGEpDQp5IDwtIHJzZ2VvOjphc19yc2dlbyhQaGlsbHlDb21Db3JyKQ0KDQojIyBDYWxjdWxhdGluZyBkaXN0YW5jZQ0KDQpkYXRhJGRpc3RfdG9fY29tbV9jb3JyIDwtIHJzZ2VvOjpkaXN0YW5jZV9ldWNsaWRlYW5fcGFpcndpc2UoeCwgeVtuZWFyZXN0X2NvcnJpZG9yXSkNCg0KIyBMaXR0ZXIgSW5kZXgNCiMjIEFkZGluZyBkYXRhIG9uIFBoaWxhZGVscGhpYSdzIExpdHRlciBJbmRleA0KDQpQaGlsbHlMaXR0ZXIgPC0NCiAgc3RfcmVhZCgiLi9kYXRhL0xpdHRlcl9JbmRleC5nZW9qc29uIikgJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnModHJhY3RzMjEpKQ0KDQojIyBKb2luaW5nIHRoZSBsaXR0ZXIgc2NvcmUNCg0KZGF0YSA8LSANCiBzdF9qb2luKGRhdGEsKFBoaWxseUxpdHRlciAlPiUNCiAgICAgICAgICBzZWxlY3QoLU9CSkVDVElELCAtU2hhcGVfX0FyZWEsIC1TaGFwZV9fTGVuZ3RoICklPiUNCiAgICAgICAgICByZW5hbWUobGl0dGVyID0gU0NPUkUpKSkgDQoNCiNOZWFyZXN0IEZsb29kIFBsYWlucw0KIyMgQWRkaW5nIGRhdGEgb24gUGhpbGFkZWxwaGlhJ3MgRmxvb2QgUGxhaW4NCg0KUGhpbGx5Rmxvb2QgPC0gDQogIHN0X3JlYWQoIi4vZGF0YS9GRU1BXzEwMF9mbG9vZF9QbGFpbi5nZW9qc29uIikgJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnModHJhY3RzMjEpKQ0KDQojIyBJcyBpdCB3aXRoaW4gdGhlIGZsb29kcGxhaW4/DQoNCmRhdGEkd2l0aGluX2Zsb29kIDwtIGlmZWxzZShzdF93aXRoaW4oZGF0YSwgUGhpbGx5Rmxvb2QpLCAxLCAwKQ0KDQpkYXRhIDwtIGRhdGEgJT4lDQogIG11dGF0ZSh3aXRoaW5fZmxvb2QgPSBpZmVsc2UoaXMubmEod2l0aGluX2Zsb29kKSwgMCwgMSkpDQoNCiMjIE1hcHBpbmcgbmVhcmVzdCBmbG9vZHBsYWluIA0KDQpuZWFyZXN0X2Zsb29kcGxhaW4gPC0gc2Y6OnN0X25lYXJlc3RfZmVhdHVyZShkYXRhLCBQaGlsbHlGbG9vZCkNCg0KIyMgQ29udmVydGluZyB0byByc2dlbyBnZW9tZXRyaWVzDQoNCnggPC0gcnNnZW86OmFzX3JzZ2VvKGRhdGEpDQp5IDwtIHJzZ2VvOjphc19yc2dlbyhQaGlsbHlGbG9vZCkNCg0KIyMgQ2FsY3VsYXRpbmcgZGlzdGFuY2UNCg0KZGF0YSRkaXN0X3RvX2Zsb29kcGxhaW4gPC0gcnNnZW86OmRpc3RhbmNlX2V1Y2xpZGVhbl9wYWlyd2lzZSh4LCB5W25lYXJlc3RfZmxvb2RwbGFpbl0pDQoNCiMgTmVhcmVzdCBUcmFuc2l0IFN0b3BzDQojIyBBZGRpbmcgZGF0YSBvbiBQaGlsYWRlbHBoaWEncyBUcmFuc2l0IFN0b3BzDQoNCmVsIDwtIHN0X3JlYWQoImh0dHBzOi8vb3BlbmRhdGEuYXJjZ2lzLmNvbS9kYXRhc2V0cy84YzZlMjU3NWM4YWQ0NmViODg3ZTZiYjM1ODI1ZTFhNl8wLmdlb2pzb24iKQ0KQnJvYWRfU3QgPC0gc3RfcmVhZCgiaHR0cHM6Ly9vcGVuZGF0YS5hcmNnaXMuY29tL2RhdGFzZXRzLzJlOTAzN2ZkNWJlZjQwNjQ4OGZmZTViYjY3ZDIxMzEyXzAuZ2VvanNvbiIpDQoNClBoaWxseVNlcHRhU3RvcHMgPC0gDQogIHJiaW5kKA0KICAgICBlbCAlPiUgDQogICAgICBtdXRhdGUoTGluZSA9ICJFbCIpICU+JQ0KICAgICAgZHBseXI6OnNlbGVjdChTdGF0aW9uLCBMaW5lKSwNCiAgICAgQnJvYWRfU3QgJT4lDQogICAgICBtdXRhdGUoTGluZSA9IkJyb2FkX1N0IikgJT4lDQogICAgICBkcGx5cjo6c2VsZWN0KFN0YXRpb24sIExpbmUpKSAlPiUNCiAgc3RfdHJhbnNmb3JtKHN0X2Nycyh0cmFjdHMyMSkpICANCg0KIyMgTWFwcGluZyBuZWFyZXN0IHRyYW5zaXQgc3RvcA0KDQpuZWFyZXN0X3N0YXRpb24gPC0gc2Y6OnN0X25lYXJlc3RfZmVhdHVyZShkYXRhLCBQaGlsbHlTZXB0YVN0b3BzKQ0KDQojIyBDb252ZXJ0aW5nIHRvIHJzZ2VvIGdlb21ldHJpZXMNCg0KeCA8LSByc2dlbzo6YXNfcnNnZW8oZGF0YSkNCnkgPC0gcnNnZW86OmFzX3JzZ2VvKFBoaWxseVNlcHRhU3RvcHMpDQoNCiMjIENhbGN1bGF0aW5nIGRpc3RhbmNlDQoNCmRhdGEkZGlzdF90b19uZWFyZXN0X3N0YXRpb24gPC0gcnNnZW86OmRpc3RhbmNlX2V1Y2xpZGVhbl9wYWlyd2lzZSh4LCB5W25lYXJlc3Rfc3RhdGlvbl0pDQoNCiMgTmVhcmVzdCBUcmFpbHMNCiMjIEFkZGluZyBkYXRhIG9uIFBoaWxhZGVscGhpYSBUcmFpbHMgDQoNClBoaWxseVRyYWlscyA8LQ0KIHN0X3JlYWQoIi4vZGF0YS9QUFJfVHJhaWxzLmdlb2pzb24iKSU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKHRyYWN0czIxKSkNCg0KIyMgTWFwcGluZyBuZWFyZXN0IHRyYWlsDQoNCm5lYXJlc3RfdHJhaWwgPC0gc2Y6OnN0X25lYXJlc3RfZmVhdHVyZShkYXRhLCBQaGlsbHlUcmFpbHMpDQoNCiMjIENvbnZlcnRpbmcgdG8gcnNnZW8gZ2VvbWV0cmllcw0KDQp4IDwtIHJzZ2VvOjphc19yc2dlbyhkYXRhKQ0KeSA8LSByc2dlbzo6YXNfcnNnZW8oUGhpbGx5VHJhaWxzKQ0KDQojIyBDYWxjdWxhdGluZyBkaXN0YW5jZQ0KDQpkYXRhJGRpc3RfdG9fbmVhcmVzdF90cmFpbCA8LSByc2dlbzo6ZGlzdGFuY2VfZXVjbGlkZWFuX3BhaXJ3aXNlKHgsIHlbbmVhcmVzdF90cmFpbF0pDQoNCiMgTmVhcmVzdCBUZW5uaXMgQ291cnRzDQojIyBBZGRpbmcgZGF0YSBvbiBQaGlsYWRlbHBoaWEgVGVubmlzIENvdXJ0cyANCg0KUGhpbGx5VGVubmlzQ291cnRzIDwtDQogc3RfcmVhZCgiLi9kYXRhL1BQUl9UZW5uaXNfQ291cnRzLmdlb2pzb24iKSU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKHRyYWN0czIxKSkNCg0KIyMgTWFwcGluZyBuZWFyZXN0IHRlbm5pcyBjb3VydA0KDQpuZWFyZXN0X3Rlbm5pc2NvdXJ0IDwtIHNmOjpzdF9uZWFyZXN0X2ZlYXR1cmUoZGF0YSwgUGhpbGx5VGVubmlzQ291cnRzKQ0KDQojIyBDb252ZXJ0aW5nIHRvIHJzZ2VvIGdlb21ldHJpZXMNCg0KeCA8LSByc2dlbzo6YXNfcnNnZW8oZGF0YSkNCnkgPC0gcnNnZW86OmFzX3JzZ2VvKFBoaWxseVRlbm5pc0NvdXJ0cykNCg0KIyMgQ2FsY3VsYXRpbmcgZGlzdGFuY2UNCg0KZGF0YSRkaXN0X3RvX25lYXJlc3RfdGVubmlzY291cnQgPC0gcnNnZW86OmRpc3RhbmNlX2V1Y2xpZGVhbl9wYWlyd2lzZSh4LCB5W25lYXJlc3RfdGVubmlzY291cnRdKQ0KDQojIE5lYXJlc3QgUGxheWdyb3VuZHMNCiMjIEFkZGluZyBkYXRhIG9uIFBoaWxhZGVscGhpYSBQbGF5Z3JvdW5kcyANCg0KUGhpbGx5UGxheWdyb3VuZHMgPC0NCiBzdF9yZWFkKCIuL2RhdGEvUFBSX1BsYXlncm91bmRzLmdlb2pzb24iKSU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKHRyYWN0czIxKSkNCg0KIyMgTWFwcGluZyBuZWFyZXN0IHBsYXlncm91bmQNCg0KbmVhcmVzdF9wbGF5Z3JvdW5kIDwtIHNmOjpzdF9uZWFyZXN0X2ZlYXR1cmUoZGF0YSwgUGhpbGx5UGxheWdyb3VuZHMpDQoNCiMjIENvbnZlcnRpbmcgdG8gcnNnZW8gZ2VvbWV0cmllcw0KDQp4IDwtIHJzZ2VvOjphc19yc2dlbyhkYXRhKQ0KeSA8LSByc2dlbzo6YXNfcnNnZW8oUGhpbGx5UGxheWdyb3VuZHMpDQoNCiMjIENhbGN1bGF0aW5nIGRpc3RhbmNlDQoNCmRhdGEkZGlzdF90b19uZWFyZXN0X3BsYXlncm91bmQgPC0gcnNnZW86OmRpc3RhbmNlX2V1Y2xpZGVhbl9wYWlyd2lzZSh4LCB5W25lYXJlc3RfcGxheWdyb3VuZF0pDQoNCiMgTmVhcmVzdCBQb29scw0KIyMgQWRkaW5nIGRhdGEgb24gUGhpbGFkZWxwaGlhIFBvb2xzIA0KDQpQaGlsbHlQb29scyA8LQ0KIHN0X3JlYWQoIi4vZGF0YS9QUFJfU3dpbW1pbmdfUG9vbHMuZ2VvanNvbiIpJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnModHJhY3RzMjEpKQ0KDQojIyBNYXBwaW5nIG5lYXJlc3QgcG9vbA0KDQpuZWFyZXN0X3Bvb2wgPC0gc2Y6OnN0X25lYXJlc3RfZmVhdHVyZShkYXRhLCBQaGlsbHlQb29scykNCg0KIyMgQ29udmVydGluZyB0byByc2dlbyBnZW9tZXRyaWVzDQoNCnggPC0gcnNnZW86OmFzX3JzZ2VvKGRhdGEpDQp5IDwtIHJzZ2VvOjphc19yc2dlbyhQaGlsbHlQb29scykNCg0KIyMgQ2FsY3VsYXRpbmcgZGlzdGFuY2UNCg0KZGF0YSRkaXN0X3RvX25lYXJlc3RfcG9vbCA8LSByc2dlbzo6ZGlzdGFuY2VfZXVjbGlkZWFuX3BhaXJ3aXNlKHgsIHlbbmVhcmVzdF9wb29sXSkNCg0KIyBOZWFyZXN0IFBvbGljZSBTdGF0aW9ucw0KIyMgQWRkaW5nIGRhdGEgb24gUGhpbGFkZWxwaGlhIFBvbGljZSBTdGF0aW9ucw0KDQpQaGlsbHlQb2xpY2VTdGF0aW9ucyA8LQ0KIHN0X3JlYWQoIi4vZGF0YS9Qb2xpY2VfU3RhdGlvbnMuZ2VvanNvbiIpJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnModHJhY3RzMjEpKQ0KDQojIyBNYXBwaW5nIG5lYXJlc3QgcG9saWNlIHN0YXRpb24NCg0KbmVhcmVzdF9wb2xpY2Vfc3RhdGlvbiA8LSBzZjo6c3RfbmVhcmVzdF9mZWF0dXJlKGRhdGEsIFBoaWxseVBvbGljZVN0YXRpb25zKQ0KDQojIyBDb252ZXJ0aW5nIHRvIHJzZ2VvIGdlb21ldHJpZXMNCg0KeCA8LSByc2dlbzo6YXNfcnNnZW8oZGF0YSkNCnkgPC0gcnNnZW86OmFzX3JzZ2VvKFBoaWxseVBvbGljZVN0YXRpb25zKQ0KDQojIyBDYWxjdWxhdGluZyBkaXN0YW5jZQ0KDQpkYXRhJGRpc3RfdG9fbmVhcmVzdF9wb2xpY2Vfc3RhdGlvbiA8LSByc2dlbzo6ZGlzdGFuY2VfZXVjbGlkZWFuX3BhaXJ3aXNlKHgsIHlbbmVhcmVzdF9wb2xpY2Vfc3RhdGlvbl0pDQoNCmBgYA0KDQpBIGRpc3RhbmNlIGFuYWx5c2lzIGlzIHBlcmZvcm1lZCBmb3IgZWFjaCBvZiB0aGUgYWJvdmUgYW1lbml0aWVzIGluIHJlbGF0aW9uIHRvIG91ciBob21lIHZhbHVlIGRhdGFzZXQgdG8gaWRlbnRpZnkgdGhlIG5lYXJlc3QgYW1lbml0eSBvZiBlYWNoIGtpbmQgdG8gZWFjaCBob3VzZSBpbiB0aGUgb3JpZ2luYWwgZGF0YXNldC4gVGhpcyBhbmFseXNpcyBpcyBjb25kdWN0ZWQgYXMgYSBwcmVwYXJhdG9yeSBzdGVwLCB0aGUgZGlzdGFuY2VzIGRlcml2ZWQgZnJvbSB0aGlzIGFuYWx5c2lzIGFyZSBzdHVkaWVkIG5leHQgdG8gZGV0ZXJtaW5lIGlmIHRoZXNlIHNwYXRpYWwgZmFjdG9ycyBwbGF5IGEgcm9sZSBpbiBpbmZsdWVuY2luZyBob21lIHByaWNlcy4NCg0KIyMjIEsgTmVhcmVzdCBOZWlnaGJvcnMgDQoNCkluIG9yZGVyIHRvIGV2YWx1YXRlIHRoZSBpbnN0YW5jZXMgb2YgZ3VuIHZpb2xlbmNlIGFyb3VuZCBhIGxpc3RlZCBob21lLCB0aGUgbWV0aG9kIG9mIGstTmVhcmVzdCBOZWlnaGJvcnMgQW5hbHlzaXMgaXMgdXNlZC4gSW4gdGhpcyBjYXNlLCBhIGhpZ2hlciB2YWx1ZSBvZiBrPTUgaXMgcHJlZmVycmVkIHRvIGFjY291bnQgZm9yIHRoZSBzcGF0aWFsIGRpc3RyaWJ1dGlvbiBvZiB0aGUgaW5zdGFuY2VzIGluIHJlbGF0aW9uIHRvIHRoZSBob21lcyBhbmQgdG8gZW5zdXJlIHRoYXQgdGhlcmUgYXJlIGVub3VnaCBuZWlnaGJvciBvYnNlcnZhdGlvbnMgZm9yIGVhY2ggbGlzdGVkIGhvbWUgZm9yIGJldHRlciBiYWxhbmNlIGFjcm9zcyB0aGUgZGF0YXNldC4gQSBoaWdoZXIgayB2YWx1ZSBpcyBhbHNvIHByZWZlcnJlZCBmb3IgdGhlIHJvYnVzdG5lc3MgYW5kIGhpZ2hlciBnZW5lcmFsaXphYmlsaXR5IGl0IG9mZmVycyBkdXJpbmcgcHJlZGljdGlvbi4NCg0KYGBge3J9DQojIFBoaWxhZGVscGhpYSBTaG9vdGluZ3MNCiMjIEFkZGluZyBkYXRhIG9uIFBoaWxhZGVscGhpYSBTaG9vdGluZ3MNCg0KUGhpbGx5U2hvb3RpbmdzIDwtDQogc3RfcmVhZCgiLi9kYXRhL3Nob290aW5nczIwMjEuZ2VvanNvbiIpJT4lDQogIGRwbHlyOjpzZWxlY3QoImRhdGVfIiwgInBvaW50X3giLCAicG9pbnRfeSIsICJnZW9tZXRyeSIpJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnMoZGF0YSkpJT4lDQogIG5hLm9taXQoKSANCg0KIyNjYWxjdWxhdGluZyBkaXN0YW5jZSB0byBuZWFyZXN0IG5laWdoYm9ycw0KDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShzcGF0c3RhdCkNCmxpYnJhcnkoY2xhc3MpDQoNCmRhdGFfcHBwIDwtIGFzLnBwcChkYXRhKQ0KUGhpbGx5U2hvb3RpbmdzX3BwcCA8LSBhcy5wcHAoUGhpbGx5U2hvb3RpbmdzKQ0KDQpkYXRhJGNyaW1lX25uNSA8LSBubmNyb3NzKGRhdGFfcHBwLCBQaGlsbHlTaG9vdGluZ3NfcHBwLCBrID0gNSkNCg0KYGBgDQoNCmBgYHtyIFByaWNlL1NxZnQsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KDQp0cmFjdHMyMSA8LSANCiAgdHJhY3RzMjEgJT4lDQogIG11dGF0ZShQY3RXaGl0ZSA9ICgocmFjZV93aGl0ZS90b3RhbHBvcCkqMTAwKSwNCiAgICAgICAgIFBjdEJsYWNrID0gKChyYWNlX2JsYWNrL3RvdGFscG9wKSoxMDApLA0KICAgICAgICAgUGN0SGlzcGFuaWMgPSAoKGhpc3BhbmljX2xhdGluby90b3RhbHBvcCkqMTAwKSwNCiAgICAgICAgIFBjdEJhY2hlbG9ycyA9ICgoZWR1X2JhY2hlbG9ycy90b3RhbHBvcCkqMTAwKSwNCiAgICAgICAgIFBjdFBvdmVydHkgPSAoKHBvcF9iZWxvd19wb3ZlcnR5L3RvdGFscG9wKSoxMDApKQ0KDQpgYGANCg0KDQojIyBKb2luaW5nIERhdGEgDQoNCkluIG9yZGVyIHRvIGJlZ2luIGNvbmR1Y3RpbmcgZXhwbG9yYXRvcnkgYW5hbHlzZXMgb2YgdGhlIHJlbGF0aW9ucyB3aXRoaW4gdGhlIGRhdGFzZXQsIHRoZSBmb3VuZGF0aW9uYWwgZGF0YXNldCBpcyBqb2luZWQgd2l0aCB0aGUgZGF0YXNldHMgY29udGFpbmluZyByZWxldmFudCBzcGF0aWFsIGFuZCBkZW1vZ3JhcGhpYyBhdHRyaWJ1dGVzLg0KDQoNCmBgYHtyIGNlbnN1cyBqb2lufQ0KDQojIGpvaW5pbmcgY2Vuc3VzIGRhdGENCg0KZGF0YSA8LSANCiAgc3Rfam9pbihkYXRhLCB0cmFjdHMyMSkNCg0KYGBgDQoNCmBgYHtyIHNhdmUgZm9yIHByZWRpY3Rpb259DQoNCiNzYXZpbmcgZm9yIHByZWRpY3Rpb24gDQoNCmRhdGFfb2cgPC0gZGF0YQ0KDQpkYXRhIDwtIGRhdGEgJT4lDQogIGZpbHRlcihzYWxlX3ByaWNlID4gMCkgDQoNCmBgYA0KDQoNCiMgRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcw0KDQpQcm92aWRlZCBiZWxvdyBhcmUgdGhlIHN1bW1hcnkgc3RhdGlzdGljcyBmb3IgdGhlIGRpZmZlcmVudCB0eXBlcyBvZiB2YXJpYWJsZXMgY29uc2lkZXJlZCAoaW50ZXJuYWwsIHNwYXRpYWwsIGFuZCBkZW1vZ3JhcGhpYykuIFRoZSBleHBsb3JhdGlvbiBmdXJ0aGVyIGxvb2tzIGF0IHRoZSBkaXN0cmlidXRpb24gb2Ygbm9uLW51bWVyaWMgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHRocm91Z2ggYmFyIHBsb3RzLiBJdCBmaW5hbGx5IGluc3BlY3RzIHRoZSBkZXBlbmRlbmNlIG9mICdob21lIHNhbGUgcHJpY2UnIG9uIG90aGVyIGNvbnRpbnVvdXMgaW5kZXBlbmRlbnQgdmFyaWFibGVzIGluY2x1ZGluZyB0b3RhbCBhcmVhLCBtZWRpYW4gaW5jb21lLCBhbmQgcmFjaWFsIGRpc3RyaWJ1dGlvbiBhbW9uZyBvdGhlcnMuIA0KDQojIyBTdW1tYXJ5IFN0YXRpc3RpY3M6IEludGVybmFsIFZhcmlhYmxlcw0KDQpUaGVzZSBzdGF0aXN0aWNzIHByb3ZpZGUgYW4gb3ZlcnZpZXcgb2YgdGhlIGNlbnRyYWwgdGVuZGVuY3ksIHNwcmVhZCwgYW5kIHJhbmdlIG9mIHRoZSBpbnRlcm5hbCB2YXJpYWJsZXMgZm9yIFBoaWxhZGVscGhpYSBob21lcywgd2hpY2ggaW5jbHVkZSBzYWxlIHByaWNlcywgcHJpY2UgcGVyIHNxdWFyZSBmb290LCBsaXZpbmcgYXJlYSBzaXplLCB5ZWFyIGJ1aWx0LCBudW1iZXIgb2Ygcm9vbXMsIG51bWJlciBvZiBiYXRocm9vbXMsIGFuZCBudW1iZXIgb2YgYmVkcm9vbXMuDQoNCg0KYGBge3Igc3VtbSBzdGF0IGludH0NCkludGVybmFsVmFyaWFibGVzIDwtIGRhdGEgDQoNCkludGVybmFsVmFyaWFibGVzIDwtIHN0X2Ryb3BfZ2VvbWV0cnkoSW50ZXJuYWxWYXJpYWJsZXMpDQoNCkludGVybmFsVmFyaWFibGVzIDwtIEludGVybmFsVmFyaWFibGVzICU+JQ0KICBkcGx5cjo6c2VsZWN0KCJzYWxlX3ByaWNlIiwgIlByaWNlUGVyU3FmdCIsICJ0b3RhbF9saXZhYmxlX2FyZWEiLCAieWVhcl9idWlsdCIsICJudW1iZXJfb2Zfcm9vbXMiLCAibnVtYmVyX29mX2JhdGhyb29tcyIsICJudW1iZXJfb2ZfYmVkcm9vbXMiKSANCg0Kc3RhcmdhemVyKGFzLmRhdGEuZnJhbWUoSW50ZXJuYWxWYXJpYWJsZXMpLCB0eXBlPSJ0ZXh0IiwgZGlnaXRzPTEsIHRpdGxlID0gIkRlc2NyaXB0aXZlIFN0YXRpc3RpY3MgZm9yIFBoaWxhZGVscGhpYSBIb21lcyBJbnRlcm5hbCBWYXJpYWJsZXMgKEZpZ3VyZSA0LjEpIiwgb3V0ID0gIlRyYWluaW5nX1BITEludGVybmFsLnR4dCIpDQpgYGANCg0KSXQgaXMgb2JzZXJ2ZWQgdGhhdCB0aGUgYXZlcmFnZSBzYWxlIHByaWNlIG9mIGhvbWVzIGluIFBoaWxhZGVscGhpYSBpcyBhcHByb3hpbWF0ZWx5IDI4Miw4NTcuOCBVU0QuIFRoZSBzYWxlIHByaWNlcyB2YXJ5IHF1aXRlIHdpZGVseSB3aXRoIGEgaGlnaCBzdGFuZGFyZCBkZXZpYXRpb24sIGluZGljYXRpbmcgc2lnbmlmaWNhbnQgZGlzcGVyc2lvbiBpbiBwcmljZXMuIEl0IGlzIGFsc28gb2JzZXJ2ZWQgdGhhdCB0aGUgYXZlcmFnZSBsaXZhYmxlIGFyZWEgaW4gUGhpbGFkZWxwaGlhIGlzIGFwcHJveGltYXRlbHkgMTMzNCBzcWZ0IHdpdGggdGhlIHByaWNlIHBlciBzcXVhcmUgZm9vdCBmb3IgaG9tZXMgcG9zaXRpb25lZCBhdCBhYm91dCAyMDkuMyBVU0QuIA0KDQpUaGUgYXZlcmFnZSBhZ2UgZm9yIGEgaG91c2UgaW4gUGhpbGFkZWxwaGlhIGlzIGFwcHJveGltYXRlbHkgODUgeWVhcnMgaW5kaWNhdGluZyBhIGxhcmdlIHNoYXJlIG9mIHRoZSBtYXJrZXQgaXMgaGVsZCBieSBob21lcyBjb25zdHJ1Y3RlZCBvdmVyIGEgY2VudHVyeSBhZ28uIA0KDQpGaW5hbGx5LCBob21lcyBpbiBQaGlsYWRlbHBoaWEgb24gYXZlcmFnZSBzZWVtIHRvIGhhdmUgNiByb29tcyBvdmVyYWxsIHdpdGggYXBwcm94aW1hdGVseSAzIGJlZHJvb21zIGFuZCAxIGJhdGhyb29tLiANCg0KIyMgU3VtbWFyeSBTdGF0aXN0aWNzOiBEZW1vZ3JhcGhpYyBWYXJpYWJsZXMNCg0KVGhlc2Ugc3RhdGlzdGljcyBwcm92aWRlIGFuIG92ZXJ2aWV3IG9mIHRoZSBjZW50cmFsIHRlbmRlbmN5LCBzcHJlYWQsIGFuZCByYW5nZSBvZiB0aGUgaW50ZXJuYWwgdmFyaWFibGVzIGZvciBQaGlsYWRlbHBoaWEgaG9tZXMsIHdoaWNoIGluY2x1ZGUgbWVkaWFuIGluY29tZSwgcGVyY2VudGFnZSBvZiB3aGl0ZSBwb3B1bGF0aW9uLCBwZXJjZW50YWdlIG9mIGJsYWNrIHBvcHVsYXRpb24sIHBlcmNlbnRhZ2Ugb2YgaGlzcGFuaWMgcG9wdWxhdGlvbiwgcGVyY2VudGFnZSBvZiBwb3B1bGF0aW9uIHdpdGggYSBiYWNoZWxvcidzIGRlZ3JlZSBhbmQgcGVyY2VudGFnZSBvZiBwb3B1bGF0aW9uIGxpdmluZyBpbiBwb3ZlcnR5Lg0KDQoNCmBgYHtyIHN1bW0gc3RhdCBkZW19DQpEZW1vZ3JhcGhpY1ZhcmlhYmxlcyA8LSBkYXRhIA0KDQpEZW1vZ3JhcGhpY1ZhcmlhYmxlcyA8LSBzdF9kcm9wX2dlb21ldHJ5KERlbW9ncmFwaGljVmFyaWFibGVzKQ0KDQpEZW1vZ3JhcGhpY1ZhcmlhYmxlcyA8LSBEZW1vZ3JhcGhpY1ZhcmlhYmxlcyAlPiUNCiAgZHBseXI6OnNlbGVjdCgiUGN0V2hpdGUiLCAiUGN0QmxhY2siLCAiUGN0SGlzcGFuaWMiLCAiUGN0QmFjaGVsb3JzIiwgIlBjdFBvdmVydHkiLCAibWVkX2luY29tZSIpIA0KDQpzdGFyZ2F6ZXIoYXMuZGF0YS5mcmFtZShEZW1vZ3JhcGhpY1ZhcmlhYmxlcyksIHR5cGU9InRleHQiLCBkaWdpdHM9MSwgdGl0bGUgPSAiRGVzY3JpcHRpdmUgU3RhdGlzdGljcyBmb3IgUGhpbGFkZWxwaGlhIEhvbWVzIERlbW9ncmFwaGljIFZhcmlhYmxlcyAoRmlndXJlIDQuMSkiLCBvdXQgPSAiVHJhaW5pbmdfUEhMU3BhdGlhbC50eHQiKQ0KYGBgDQoNClRoZSB0YWJsZSBwcmVzZW50cyBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIGZvciBkZW1vZ3JhcGhpYyB2YXJpYWJsZXMgcmVsYXRlZCB0byBQaGlsYWRlbHBoaWEgaG9tZXMsIG9mZmVyaW5nIGluc2lnaHRzIGludG8gdGhlIGNoYXJhY3RlcmlzdGljcyBvZiB0aGUgcG9wdWxhdGlvbiBpbiBkaWZmZXJlbnQgYXJlYXMuDQoNCk9uIGF2ZXJhZ2UsIGFwcHJveGltYXRlbHkgNDQuMSUgb2YgdGhlIHBvcHVsYXRpb24gaW4gdGhlIG9ic2VydmVkIGFyZWFzIGlzIFdoaXRlLiBUaGUgcmFuZ2Ugb2YgV2hpdGUgcG9wdWxhdGlvbiBwZXJjZW50YWdlcyBzcGFucyBmcm9tIDAuMCUgdG8gOTUuNyUsIGluZGljYXRpbmcgYSB3aWRlIGRpdmVyc2l0eSBpbiByYWNpYWwgY29tcG9zaXRpb24gYWNyb3NzIGRpZmZlcmVudCBhcmVhcyBvZiBQaGlsYWRlbHBoaWEuIFRoZSBzYW1lIGhvbGRzIHRydWUgZm9yIEJsYWNrIGFuZCBIaXNwYW5pYyBwb3B1bGF0aW9uIHBlcmNlbnRhZ2VzIGluZGljYXRpbmcgdGhlIHByZXNlbmNlIG9mIFdoaXRlLW1ham9yaXR5LCBCbGFjayBtYWpvcml0eSBhbmQgSGlzcGFuaWMgbWFqb3JpdHkgbmVpZ2hib3Job29kcyBpbiBkaWZmZXJlbnQgcGFydHMgb2YgUGhpbGFkZWxwaGlhLiBPbiBhdmVyYWdlLCBhYm91dCAxNC4xJSBvZiB0aGUgcG9wdWxhdGlvbiBpbiB0aGUgb2JzZXJ2ZWQgYXJlYXMgaG9sZHMgYSBCYWNoZWxvcidzIGRlZ3JlZSBvciBoaWdoZXIuDQoNClRoZSBhdmVyYWdlIG1lZGlhbiBob3VzZWhvbGQgaW5jb21lIGluIHRoZSBvYnNlcnZlZCBhcmVhcyBpcyAkNjEsMzIwLjAuIEhvd2V2ZXIsIGFwcHJveGltYXRlbHkgMjAuNCUgb2YgdGhlIHBvcHVsYXRpb24gaW4gdGhlIG9ic2VydmVkIGFyZWFzIGZhbGxzIGJlbG93IHRoZSBwb3ZlcnR5IGxldmVsLCBvbiBhdmVyYWdlLg0KDQojIyBTdW1tYXJ5IFN0YXRpc3RpY3M6IFNwYXRpYWwgVmFyaWFibGVzDQoNClRoZXNlIHN0YXRpc3RpY3MgcHJvdmlkZSBhbiBvdmVydmlldyBvZiB0aGUgY2VudHJhbCB0ZW5kZW5jeSwgc3ByZWFkLCBhbmQgcmFuZ2Ugb2YgdGhlIGludGVybmFsIHZhcmlhYmxlcyBmb3IgUGhpbGFkZWxwaGlhIGhvbWVzLCB3aGljaCBpbmNsdWRlIGRpc3RhbmNlcyB0byBrZXkgbmVhcmVzdCBhbWVuaXRpZXMgYW5kIHRocmVhdGVuaW5nIGFjdGl2aXRpZXMgaW5jbHVkaW5nIG5lYXJlc3Qgc2Nob29scywgbmVhcmVzdCBwcml2YXRlIHNjaG9vbHMsIG5lYXJlc3QgbGFuZG1hcmtzLCBuZWFyZXN0IGNvbW1lcmNpYWwgY29ycmlkb3JzLCBuZWFyZXN0IHRyYWlscywgbmVhcmVzdCBwb2xpY2Ugc3RhdGlvbnMsIGFuZCBuZWFyZXN0IGNyaW1lIGluc3RhbmNlcy4NCg0KDQpgYGB7ciBzdW1tIHN0YXQgc3BhdH0NCg0KU3BhdGlhbFZhcmlhYmxlcyA8LSBkYXRhIA0KDQpTcGF0aWFsVmFyaWFibGVzIDwtIHN0X2Ryb3BfZ2VvbWV0cnkoU3BhdGlhbFZhcmlhYmxlcykNCg0KU3BhdGlhbFZhcmlhYmxlcyA8LSBTcGF0aWFsVmFyaWFibGVzICU+JQ0KICBkcGx5cjo6c2VsZWN0KCJkaXN0X3RvX25lYXJlc3Rfc2Nob29sIiwgImRpc3RfdG9fbmVhcmVzdF9wdnRfc2Nob29sIiwgImRpc3RfdG9fbmVhcmVzdF9sYW5kbWFyayIsICJkaXN0X3RvX2NvbW1fY29yciIsICJkaXN0X3RvX25lYXJlc3Rfc3RhdGlvbiIsICJkaXN0X3RvX25lYXJlc3RfdHJhaWwiLCAiZGlzdF90b19uZWFyZXN0X3BvbGljZV9zdGF0aW9uIiwiY3JpbWVfbm41IikgDQoNCnN0YXJnYXplcihhcy5kYXRhLmZyYW1lKFNwYXRpYWxWYXJpYWJsZXMpLCB0eXBlPSJ0ZXh0IiwgZGlnaXRzPTEsIHRpdGxlID0gIkRlc2NyaXB0aXZlIFN0YXRpc3RpY3MgZm9yIFBoaWxhZGVscGhpYSBIb21lcyBTcGF0aWFsIFZhcmlhYmxlcyAoRmlndXJlIDQuMSkiLCBvdXQgPSAiVHJhaW5pbmdfUEhMU3BhdGlhbC50eHQiKQ0KYGBgDQpUaGVzZSBzdGF0aXN0aWNzIG9mZmVyIGluc2lnaHRzIGludG8gdGhlIHNwYXRpYWwgY2hhcmFjdGVyaXN0aWNzIG9mIFBoaWxhZGVscGhpYSBuZWlnaGJvcmhvb2RzIHdoZXJlIHRoZSAiTWVhbiIgaW4gZWFjaCBjYXNlIHJlcHJlc2VudHMgdGhlIGF2ZXJhZ2UgZGlzdGFuY2UgdG8gYSBzcGVjaWZpYyBhbWVuaXR5IGZyb20gYSBsaXN0ZWQgcHJvcGVydHkuIA0KDQojIyBQbG90dGluZyBDYXRlZ29yaWNhbCBWYXJpYWJsZXMgDQoNClRoZSBmb2xsb3dpbmcgZXhwbG9yYXRvcnkgYW5hbHlzaXMgcGxvdCwgdGl0bGVkICJQcmljZSBhcyBhIGZ1bmN0aW9uIG9mIENhdGVnb3JpY2FsIFZhcmlhYmxlcywiIGlzIGEgc2V0IG9mIGdyb3VwZWQgYmFyIGNoYXJ0cy4gRWFjaCBjaGFydCBjb3JyZXNwb25kcyB0byBhIHNlbGVjdGVkIGNhdGVnb3JpY2FsIHZhcmlhYmxlLCBhbmQgd2l0aGluIGVhY2ggY2hhcnQsIGRpZmZlcmVudCBjYXRlZ29yaWVzIG9mIHRoYXQgdmFyaWFibGUgYXJlIGNvbXBhcmVkIGJhc2VkIG9uIHRoZWlyIGluZmx1ZW5jZSBvbiB0aGUgc2FsZSBwcmljZSBvZiBob21lcy4gVGhpcyB2aXN1YWxpemF0aW9uIGhlbHBzIGFzc2VzcyBob3cgZWFjaCBjYXRlZ29yaWNhbCB2YXJpYWJsZSByZWxhdGVzIHRvIGhvbWUgc2FsZSBwcmljZXMgYW5kIGFsbG93cyBmb3IgdmlzdWFsIGNvbXBhcmlzb24gYWNyb3NzIGNhdGVnb3JpZXMuIFRoaXMgYWxsb3dzIGEgY2xlYXIgY29tcGFyaXNvbiBvZiBtdWx0aXBsZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgYXQgb25jZSwgbWFraW5nIGl0IGVhc2llciB0byBpZGVudGlmeSB0cmVuZHMgYW5kIHBhdHRlcm5zIGluIHRoZSBkYXRhLg0KDQpgYGB7ciBjYXQgcGxvdCwgZmlnLmhlaWdodD0yMCwgZmlnLndpZHRoPTE0fQ0KQ2F0ZWdvcmljYWxWYXJpYWJsZXMgPC0gZGF0YSANCkNhdGVnb3JpY2FsVmFyaWFibGVzIDwtIHN0X2Ryb3BfZ2VvbWV0cnkoQ2F0ZWdvcmljYWxWYXJpYWJsZXMpDQpDYXRlZ29yaWNhbFZhcmlhYmxlcyA8LSBDYXRlZ29yaWNhbFZhcmlhYmxlcyAlPiUNCiAgZHBseXI6OnNlbGVjdCgiQmFzZW1lbnRUeXBlIiwgIkdhcmFnZVR5cGUiLCAiVmlld1R5cGUiLCAiRXh0ZXJpb3JDb25kaXRpb25UeXBlIiwgIkludGVyaW9yQ29uZGl0aW9uVHlwZSIsICJidWlsZGluZ19jb2RlX2Rlc2NyaXB0aW9uX25ldyIsInNhbGVfcHJpY2UiKSANCg0KQ2F0ZWdvcmljYWxWYXJpYWJsZXMgJT4lDQogIGdhdGhlcihDYXRlZ29yaWNhbFZhcmlhYmxlcywgVmFsdWUsIC1zYWxlX3ByaWNlKSAlPiUNCiAgZ2dwbG90KGFlcyhWYWx1ZSwgc2FsZV9wcmljZSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKw0KICBmYWNldF93cmFwKH5DYXRlZ29yaWNhbFZhcmlhYmxlcywgc2NhbGVzID0gImZyZWUiLCBuY29sPTIpICsNCiAgbGFicyh0aXRsZSA9ICJQcmljZSBhcyBhIGZ1bmN0aW9uIG9mIENhdGVnb3JpY2FsIFZhcmlhYmxlcyIpICsNCiAgcGxvdFRoZW1lKCkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KDQpgYGANCg0KDQojIEV4cGxvcmF0b3J5IE1hcHBpbmcgYW5kIEFuYWx5c2lzIA0KDQojIyBNYXBwaW5nIEludGVybmFsIFZhcmlhYmxlcw0KDQpgYGB7ciBtYXAgaW50fQ0KDQojI01hcHBpbmcgSW50ZXJuYWwgVmFyaWFibGVzDQojIE1hcHBpbmcgc2FsZSBwcmljZQ0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImRhcmtncmV5IikgKw0KICBnZW9tX3NmKGRhdGEgPSBkYXRhLCBhZXMoY29sb3VyID0gcTUoc2FsZV9wcmljZSkpLCANCiAgICAgICAgICBzaG93LmxlZ2VuZCA9ICJwb2ludCIsIHNpemUgPSAuNzUpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlZSwNCiAgICAgICAgICAgICAgICAgICBsYWJlbHM9cUJyKGRhdGEsICJzYWxlX3ByaWNlIiksDQogICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGUgQnJlYWtzOlxuIFNhbGUgUHJpY2UiLCANCiAgICAgICAgICAgICAgICAgICBuYS52YWx1ZSA9IE5BKSArDQogIGxhYnModGl0bGU9IlByb3BlcnRpZXMgYnkgU2FsZSBQcmljZSIsIHN1YnRpdGxlID0gIlBoaWxhZGVscGhpYSAyMDIyLTIwMjMiLCANCiAgICAgIGNhcHRpb249IkZpZ3VyZSAzLjEuMSIpICsNCiAgbWFwVGhlbWUoKQ0KDQojIE1hcHBpbmcgU2l6ZQ0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImRhcmtncmV5IikgKw0KICBnZW9tX3NmKGRhdGEgPSBkYXRhLCBhZXMoY29sb3VyID0gcTUodG90YWxfbGl2YWJsZV9hcmVhKSksIA0KICAgICAgICAgIHNob3cubGVnZW5kID0gInBvaW50Iiwgc2l6ZSA9IC43NSkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGVlLA0KICAgICAgICAgICAgICAgICAgIGxhYmVscz1xQnIoZGF0YSwgInRvdGFsX2xpdmFibGVfYXJlYSIpLA0KICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlIEJyZWFrczpcbkFyZWEgaW4gU3EgRnQiLCANCiAgICAgICAgICAgICAgICAgICBuYS52YWx1ZSA9IE5BKSArDQogIGxhYnModGl0bGU9IlByb3BlcnRpZXMgYnkgU2l6ZSIsIHN1YnRpdGxlID0gIlBoaWxhZGVscGhpYSAyMDIyLTIwMjMiLCANCiAgICAgIGNhcHRpb249IkZpZ3VyZSAzLjEuMiIpICsNCiAgbWFwVGhlbWUoKQ0KDQojIE1hcHBpbmcgSW50ZXJpb3IgQ29uZGl0aW9uDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gdHJhY3RzMjEsIGZpbGwgPSAiZ3JleTg5IiwgY29sb3IgPSAiZGFya2dyZXkiKSArDQogIGdlb21fc2YoZGF0YSA9IGRhdGEsIGFlcyhjb2xvdXIgPSAoSW50ZXJpb3JDb25kaXRpb25UeXBlKSksIA0KICAgICAgICAgIHNob3cubGVnZW5kID0gInBvaW50Iiwgc2l6ZSA9IC41KSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZWUsIG5hbWU9IkludGVyaW9yIENvbmRpdGlvbiIsDQogICAgICAgICAgICAgICAgICAgbmEudmFsdWUgPSBOQSkgKw0KICBsYWJzKHRpdGxlPSJQcm9wZXJ0aWVzIGJ5IEludGVybmFsIENvbmRpdGlvbiIsIHN1YnRpdGxlID0gIlBoaWxhZGVscGhpYSAyMDIyLTIwMjMiLCANCiAgICAgIGNhcHRpb249IkZpZ3VyZSAzLjEuMyIpICsNCiAgbWFwVGhlbWUoKQ0KDQpgYGANCg0KDQojIyBNYXBwaW5nIFNwYXRpYWwgVmFyaWFibGVzDQoNCmBgYHtyIG1hcCBzcGF0fQ0KIyMgTWFwcGluZyBTcGF0aWFsIFZhcmlhYmxlcw0KDQojIE1hcHBpbmcgcHJvcGVydGllcyBhcm91bmQgc2Nob29scw0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImRhcmtncmV5IikgKw0KICBnZW9tX3NmKGRhdGEgPSBkYXRhLCBhZXMoY29sb3VyID0gcTUoc2FsZV9wcmljZSkpLCANCiAgICAgICAgICBzaG93LmxlZ2VuZCA9ICJwb2ludCIsIHNpemUgPSAuNzUsIGFscGhhPTAuMykgKyANCiAgZ2VvbV9zZihkYXRhID0gUGhpbGx5U2Nob29scywgY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDEuNSwgYWxwaGE9MC42KSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZWUsDQogICAgICAgICAgICAgICAgICAgbGFiZWxzPXFCcihkYXRhLCAic2FsZV9wcmljZSIpLA0KICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlIEJyZWFrczpcblNhbGUgUHJpY2VzIiwgDQogICAgICAgICAgICAgICAgICAgbmEudmFsdWUgPSBOQSkgKyANCiAgbGFicyh0aXRsZT0iUHJvcGVydGllcyBBcm91bmQgU2Nob29scyIsIHN1YnRpdGxlID0gIlBoaWxhZGVscGhpYSAyMDIyLTIwMjMiLCANCiAgICAgIGNhcHRpb249IkZpZ3VyZSAzLjIuMSIpICsNCiAgbWFwVGhlbWUoKQ0KDQojIE1hcHBpbmcgcHJvcGVydGllcyBhcm91bmQgY29tbWVyY2lhbCBjb3JyaWRvcnMNCg0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSB0cmFjdHMyMSwgZmlsbCA9ICJncmV5ODkiLCBjb2xvciA9ICJkYXJrZ3JleSIpICsNCiAgZ2VvbV9zZihkYXRhID0gZGF0YSwgYWVzKGNvbG91ciA9IHE1KHNhbGVfcHJpY2UpKSwgDQogICAgICAgICAgc2hvdy5sZWdlbmQgPSAicG9pbnQiLCBzaXplID0gLjc1LCBhbHBoYT0wLjMpICsgDQogIGdlb21fc2YoZGF0YSA9IFBoaWxseUNvbUNvcnIsIGNvbG91ciA9ICJibGFjayIsIGZpbGw9ImJsYWNrIiwgYWxwaGE9MC42KSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZWUsDQogICAgICAgICAgICAgICAgICAgbGFiZWxzPXFCcihkYXRhLCAic2FsZV9wcmljZSIpLA0KICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlIEJyZWFrczpcblNhbGUgUHJpY2VzIiwgDQogICAgICAgICAgICAgICAgICAgbmEudmFsdWUgPSBOQSkgKyANCiAgbGFicyh0aXRsZT0iUHJvcGVydGllcyBBcm91bmQgQ29tbWVyY2lhbCBDb3JyaWRvcnMiLCBzdWJ0aXRsZSA9ICJQaGlsYWRlbHBoaWEgMjAyMi0yMDIzIiwgDQogICAgICBjYXB0aW9uPSJGaWd1cmUgMy4yLjIiKSArDQogIG1hcFRoZW1lKCkNCg0KIyBNYXBwaW5nIHByb3BlcnRpZXMgYXJvdW5kIGxhbmRtYXJrcw0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImRhcmtncmV5IikgKw0KICBnZW9tX3NmKGRhdGEgPSBkYXRhLCBhZXMoY29sb3VyID0gcTUoc2FsZV9wcmljZSkpLCANCiAgICAgICAgICBzaG93LmxlZ2VuZCA9ICJwb2ludCIsIHNpemUgPSAuNzUsIGFscGhhPTAuMykgKyANCiAgZ2VvbV9zZihkYXRhID0gUGhpbGx5TGFuZG1hcmtzLCBjb2xvdXIgPSAiYmxhY2siLCBmaWxsPSJibGFjayIsIHNpemUgPSAuNzUsIGFscGhhPTAuNikgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGVlLA0KICAgICAgICAgICAgICAgICAgIGxhYmVscz1xQnIoZGF0YSwgInNhbGVfcHJpY2UiKSwNCiAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZSBCcmVha3M6XG5TYWxlIFByaWNlcyIsIA0KICAgICAgICAgICAgICAgICAgIG5hLnZhbHVlID0gTkEpICsgDQogIGxhYnModGl0bGU9IlByb3BlcnRpZXMgQXJvdW5kIExhbmRtYXJrcyIsIHN1YnRpdGxlID0gIlBoaWxhZGVscGhpYSAyMDIyLTIwMjMiLCANCiAgICAgIGNhcHRpb249IkZpZ3VyZSAzLjIuMyIpICsNCiAgbWFwVGhlbWUoKQ0KDQojIE1hcHBpbmcgcHJvcGVydGllcyBhcm91bmQgZmxvb2RwbGFpbnMNCg0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSB0cmFjdHMyMSwgZmlsbCA9ICJncmV5ODkiLCBjb2xvciA9ICJkYXJrZ3JleSIpICsNCiAgZ2VvbV9zZihkYXRhID0gZGF0YSwgYWVzKGNvbG91ciA9IHE1KHNhbGVfcHJpY2UpKSwgDQogICAgICAgICAgc2hvdy5sZWdlbmQgPSAicG9pbnQiLCBzaXplID0gLjc1LCBhbHBoYT0wLjMpICsgDQogIGdlb21fc2YoZGF0YSA9IFBoaWxseUZsb29kLCBjb2xvdXIgPSAiYmxhY2siLCBmaWxsPSJibGFjayIsIHNpemUgPSAuNzUsIGFscGhhPTAuNikgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGVlLA0KICAgICAgICAgICAgICAgICAgIGxhYmVscz1xQnIoZGF0YSwgInNhbGVfcHJpY2UiKSwNCiAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZSBCcmVha3M6XG5TYWxlIFByaWNlcyIsIA0KICAgICAgICAgICAgICAgICAgIG5hLnZhbHVlID0gTkEpICsgDQogIGxhYnModGl0bGU9IlByb3BlcnRpZXMgQXJvdW5kIEZsb29kIFBsYWluIiwgc3VidGl0bGUgPSAiUGhpbGFkZWxwaGlhIDIwMjItMjAyMyIsIA0KICAgICAgY2FwdGlvbj0iRmlndXJlIDMuMi40IikgKw0KICBtYXBUaGVtZSgpDQoNCiNsaWJyYXJ5KGdyaWRFeHRyYSkNCg0KI2dyaWQuYXJyYW5nZSgNCiAgI2IxLA0KICAjYjIsDQogICNiMywNCiAgI2I0LA0KICAjbnJvdyA9IDIsDQogICN3aWR0aHM9Yyg0LDQpLA0KICAjdG9wID0gIlRpdGxlIG9mIHRoZSBwYWdlIg0KICAjKQ0KDQoNCmBgYA0KDQoNCiMjIE1hcHBpbmcgRGVtb2dyYXBoaWMgVmFyaWFibGVzDQoNCmBgYHtyIG1hcCBkZW19DQojIyBNYXBwaW5nIERlbW9ncmFwaGljIFZhcmlhYmxlcw0KDQojIE1hcHBpbmcgbWVkaWFuIGluY29tZSBhcm91bmQgc29sZCBwcm9wZXJ0aWVzDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gdHJhY3RzMjEsIGZpbGwgPSAiZ3JleTg5IiwgY29sb3IgPSAiZGFya2dyZXkiKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBhZXMoZmlsbCA9IHE1KG1lZF9pbmNvbWUpKSwgY29sb3IgPSAidHJhbnNwYXJlbnQiLCBhbHBoYT0wLjUpICsNCiAgICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIkJ1UHUiLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHFCcih0cmFjdHMyMSwgIm1lZF9pbmNvbWUiKSwNCiAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIk1lZGlhbiBJbmNvbWVcblF1aW50aWxlIEJyZWFrcyIpICsNCiAgZ2VvbV9zZihkYXRhID0gZGF0YSwgYWVzKGNvbG91ciA9IHE1KHNhbGVfcHJpY2UpKSwgDQogICAgICAgICAgc2hvdy5sZWdlbmQgPSAicG9pbnQiLCBzaXplID0gLjIpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlZSwNCiAgICAgICAgICAgICAgICAgICBsYWJlbHM9cUJyKGRhdGEsICJzYWxlX3ByaWNlIiksDQogICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGUgQnJlYWtzOlxuU2FsZSBQcmljZXMiLCANCiAgICAgICAgICAgICAgICAgICBuYS52YWx1ZSA9IE5BKSArIA0KICBsYWJzKHRpdGxlPSJNZWRpYW4gSW5jb21lIEFyb3VuZCBTb2xkIFByb3BlcnRpZXMiLCBzdWJ0aXRsZSA9ICJQaGlsYWRlbHBoaWEgMjAyMi0yMDIzIiwgDQogICAgICBjYXB0aW9uPSJGaWd1cmUgMy4zLjEiKSsNCiAgbWFwVGhlbWUoKQ0KDQoNCg0KIyBNYXBwaW5nIHdoaXRlIHBvcHVsYXRpb24gYXJvdW5kIHNvbGQgcHJvcGVydGllcw0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImRhcmtncmV5IikgKw0KICBnZW9tX3NmKGRhdGEgPSB0cmFjdHMyMSwgYWVzKGZpbGwgPSBxNShQY3RXaGl0ZSkpLCBjb2xvciA9ICJ0cmFuc3BhcmVudCIsIGFscGhhPTAuNSkgKw0KICAgIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiQnVQdSIsDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcUJyKHRyYWN0czIxLCAiUGN0V2hpdGUiKSwNCiAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIiUgV2hpdGUgUG9wdWxhdGlvblxuUXVpbnRpbGUgQnJlYWtzIikgKw0KICBnZW9tX3NmKGRhdGEgPSBkYXRhLCBhZXMoY29sb3VyID0gcTUoc2FsZV9wcmljZSkpLCANCiAgICAgICAgICBzaG93LmxlZ2VuZCA9ICJwb2ludCIsIHNpemUgPSAuMikgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGVlLA0KICAgICAgICAgICAgICAgICAgIGxhYmVscz1xQnIoZGF0YSwgInNhbGVfcHJpY2UiKSwNCiAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZSBCcmVha3M6XG5TYWxlIFByaWNlcyIsIA0KICAgICAgICAgICAgICAgICAgIG5hLnZhbHVlID0gTkEpICsgDQogIGxhYnModGl0bGU9IiUgV2hpdGUgUG9wdWxhdGlvbiBBcm91bmQgU29sZCBQcm9wZXJ0aWVzIiwgc3VidGl0bGUgPSAiUGhpbGFkZWxwaGlhIDIwMjItMjAyMyIsIA0KICAgICAgY2FwdGlvbj0iRmlndXJlIDMuMy4yIikrDQogIG1hcFRoZW1lKCkNCg0KIyBNYXBwaW5nIGJsYWNrIHBvcHVsYXRpb24gYXJvdW5kIHNvbGQgcHJvcGVydGllcw0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImRhcmtncmV5IikgKw0KICBnZW9tX3NmKGRhdGEgPSB0cmFjdHMyMSwgYWVzKGZpbGwgPSBxNShQY3RCbGFjaykpLCBjb2xvciA9ICJ0cmFuc3BhcmVudCIsIGFscGhhPTAuNSkgKw0KICAgIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiQnVQdSIsDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcUJyKHRyYWN0czIxLCAiUGN0QmxhY2siKSwNCiAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIiUgQmxhY2sgUG9wdWxhdGlvblxuUXVpbnRpbGUgQnJlYWtzIikgKw0KICBnZW9tX3NmKGRhdGEgPSBkYXRhLCBhZXMoY29sb3VyID0gcTUoc2FsZV9wcmljZSkpLCANCiAgICAgICAgICBzaG93LmxlZ2VuZCA9ICJwb2ludCIsIHNpemUgPSAuMikgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGVlLA0KICAgICAgICAgICAgICAgICAgIGxhYmVscz1xQnIoZGF0YSwgInNhbGVfcHJpY2UiKSwNCiAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZSBCcmVha3M6XG5TYWxlIFByaWNlcyIsIA0KICAgICAgICAgICAgICAgICAgIG5hLnZhbHVlID0gTkEpICsgDQogIGxhYnModGl0bGU9IiUgQmxhY2sgUG9wdWxhdGlvbiBBcm91bmQgU29sZCBQcm9wZXJ0aWVzIiwgc3VidGl0bGUgPSAiUGhpbGFkZWxwaGlhIDIwMjItMjAyMyIsIA0KICAgICAgY2FwdGlvbj0iRmlndXJlIDMuMy4zIikrDQogIG1hcFRoZW1lKCkNCg0KIyBNYXBwaW5nIHBvcHVsYXRpb24gd2l0aCBiYWNoZWxvcidzIGRlZ3JlZSBhcm91bmQgc29sZCBwcm9wZXJ0aWVzDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gdHJhY3RzMjEsIGZpbGwgPSAiZ3JleTg5IiwgY29sb3IgPSAiZGFya2dyZXkiKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBhZXMoZmlsbCA9IHE1KFBjdEJhY2hlbG9ycykpLCBjb2xvciA9ICJ0cmFuc3BhcmVudCIsIGFscGhhPTAuNSkgKw0KICAgIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiQnVQdSIsDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcUJyKHRyYWN0czIxLCAiUGN0QmFjaGVsb3JzIiksDQogICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICIlIEJhY2hlbG9yJ3MgRGVncmVlXG5RdWludGlsZSBCcmVha3MiKSArDQogIGdlb21fc2YoZGF0YSA9IGRhdGEsIGFlcyhjb2xvdXIgPSBxNShzYWxlX3ByaWNlKSksIA0KICAgICAgICAgIHNob3cubGVnZW5kID0gInBvaW50Iiwgc2l6ZSA9IC4yKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZWUsDQogICAgICAgICAgICAgICAgICAgbGFiZWxzPXFCcihkYXRhLCAic2FsZV9wcmljZSIpLA0KICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlIEJyZWFrczpcblNhbGUgUHJpY2VzIiwgDQogICAgICAgICAgICAgICAgICAgbmEudmFsdWUgPSBOQSkgKyANCiAgbGFicyh0aXRsZT0iJSBQb3B1bGF0aW9uIHdpdGggQmFjaGVsb3IncyBEZWdyZWUgQXJvdW5kIFNvbGQgUHJvcGVydGllcyIsIHN1YnRpdGxlID0gIlBoaWxhZGVscGhpYSAyMDIyLTIwMjMiLCANCiAgICAgIGNhcHRpb249IkZpZ3VyZSAzLjMuNCIpKw0KICBtYXBUaGVtZSgpDQoNCiMgTWFwcGluZyBwb3B1bGF0aW9uIGluIHBvdmVydHkgYXJvdW5kIHNvbGQgcHJvcGVydGllcw0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImRhcmtncmV5IikgKw0KICBnZW9tX3NmKGRhdGEgPSB0cmFjdHMyMSwgYWVzKGZpbGwgPSBxNShQY3RQb3ZlcnR5KSksIGNvbG9yID0gInRyYW5zcGFyZW50IiwgYWxwaGE9MC41KSArDQogICAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJCdVB1IiwNCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBxQnIodHJhY3RzMjEsICJQY3RQb3ZlcnR5IiksDQogICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICIlIFBvcCBpbiBQb3ZlcnR5XG5RdWludGlsZSBCcmVha3MiKSArDQogIGdlb21fc2YoZGF0YSA9IGRhdGEsIGFlcyhjb2xvdXIgPSBxNShzYWxlX3ByaWNlKSksIA0KICAgICAgICAgIHNob3cubGVnZW5kID0gInBvaW50Iiwgc2l6ZSA9IC4yKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZWUsDQogICAgICAgICAgICAgICAgICAgbGFiZWxzPXFCcihkYXRhLCAic2FsZV9wcmljZSIpLA0KICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlIEJyZWFrczpcblNhbGUgUHJpY2VzIiwgDQogICAgICAgICAgICAgICAgICAgbmEudmFsdWUgPSBOQSkgKyANCiAgbGFicyh0aXRsZT0iJSBQb3B1bGF0aW9uIGluIFBvdmVydHkgQXJvdW5kIFNvbGQgUHJvcGVydGllcyIsIHN1YnRpdGxlID0gIlBoaWxhZGVscGhpYSAyMDIyLTIwMjMiLCANCiAgICAgIGNhcHRpb249IkZpZ3VyZSAzLjMuNSIpKw0KICBtYXBUaGVtZSgpDQoNCmBgYA0KDQoNCg0KIyBTY2F0dGVycGxvdHMgYW5kIENvcnJlbGF0aW9ucyANCg0KIyMgU2NhdHRlcnBsb3RzIA0KDQpUaGUgZm9sbG93aW5nIGNvZGUgZ2VuZXJhdGVzIGEgc2V0IG9mIHNjYXR0ZXJwbG90cyB0byBleHBsb3JlIHRoZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gdGhlIHNhbGUgcHJpY2Ugb2YgaG9tZXMgYW5kIHZhcmlvdXMgY29udGludW91cyB2YXJpYWJsZXMuICBUaGUgZGF0YSBpcyBmaXJzdCBmaWx0ZXJlZCB0byBleGNsdWRlIG9ic2VydmF0aW9ucyB3aGVyZSB0aGUgc2FsZV9wcmljZSBpcyBsZXNzIHRoYW4gMTAsMDAwLDAwMCB0byByZW1vdmUgYW55IG91dGxpZXJzLiBGb3IgdGhlIHJlbWFpbmluZyBkYXRhLCBhIHNldCBvZiBzY2F0dGVycGxvdHMgaXMgY3JlYXRlZC4gRWFjaCBzY2F0dGVycGxvdCBleGFtaW5lcyBob3cgdGhlIHNhbGVfcHJpY2UgcmVsYXRlcyB0byBkaWZmZXJlbnQgY29udGludW91cyB2YXJpYWJsZXMuIFRoZSBzY2F0dGVycGxvdHMgaGVscCB2aXN1YWxpemUgdGhlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB0aGUgc2FsZSBwcmljZSBvZiBob21lcyBhbmQgZWFjaCBvZiB0aGUgY29udGludW91cyB2YXJpYWJsZXMsIGFzIHdlbGwgYXMgdGhlIGRpcmVjdGlvbiBhbmQgc3RyZW5ndGggb2YgdGhlc2UgcmVsYXRpb25zaGlwcy4gVGhlIHJlZ3Jlc3Npb24gbGluZXMgaW5kaWNhdGUgd2hldGhlciB0aGVyZSBpcyBhIGxpbmVhciB0cmVuZCBpbiB0aGUgZGF0YS4NCg0KYGBge3Igc2NhdHRlcnBsb3RzLCBmaWcuYWxpZ249ImNlbnRlciIsIHdhcm5pbmc9RkFMU0UsIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gMTQsIGZpZy5oZWlnaHQ9MjggfQ0Kb3B0aW9ucyhzY2lwZW4gPSA5OTk5KSAjdHVybnMgb2ZmIHNjaWVudGlmaWMgbm90YXRpb24gYW5kIGhvdyBtYW55IHBvaW50cw0KDQpWYXJpYWJsZSA8LSBkYXRhICU+JQ0KICBmaWx0ZXIoc2FsZV9wcmljZSA8IDEwMDAwMDAwKQ0KDQojIyBTY2F0dGVycGxvdA0Kc3RfZHJvcF9nZW9tZXRyeShWYXJpYWJsZSkgJT4lIA0KICBkcGx5cjo6c2VsZWN0KHNhbGVfcHJpY2UsIFByaWNlUGVyU3FmdCwgeWVhcl9idWlsdCwgbnVtYmVyX29mX2JhdGhyb29tcywgbnVtYmVyX29mX2JlZHJvb21zLCBudW1iZXJfb2Zfcm9vbXMsIHRvdGFsX2xpdmFibGVfYXJlYSwgbWVkX2luY29tZSwgUGN0V2hpdGUsUGN0QmxhY2ssIFBjdEhpc3BhbmljLCBQY3RCYWNoZWxvcnMsIFBjdFBvdmVydHksICkgJT4lIA0KICBmaWx0ZXIoc2FsZV9wcmljZSA8IDEwMDAwMDAwKSAlPiUNCiAgZ2F0aGVyKFZhcmlhYmxlLCBWYWx1ZSwgLXNhbGVfcHJpY2UpICU+JSANCiAgIGdncGxvdChhZXMoVmFsdWUsIHNhbGVfcHJpY2UpKSArDQogICAgIGdlb21fcG9pbnQoc2l6ZSA9IC41KSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUYsIGNvbG91ciA9ICIjRkE3ODAwIikgKw0KICAgICBmYWNldF93cmFwKH5WYXJpYWJsZSwgbmNvbCA9IDIsIHNjYWxlcyA9ICJmcmVlIikgKw0KICAgICBsYWJzKHRpdGxlID0gIlByaWNlIGFzIGEgZnVuY3Rpb24gb2YgY29udGludW91cyB2YXJpYWJsZXMiLCANCiAgICAgICAgICBjYXB0aW9uPSJGaWd1cmUgNC4xIikgKw0KICAgICBwbG90VGhlbWUoKQ0KYGBgDQoNCiMjIENvcnJlbGF0aW9ucw0KDQpUaGUgcHJvY2VzcyBvZiB2YXJpYWJsZSBzZWxlY3Rpb24gZm9yIHRoZSBtb2RlbCB3YXMgYW4gaXRlcmF0aXZlIG9uZSwgYmVnaW5uaW5nIHdpdGggYW4gaW5pdGlhbCBleHBsb3JhdGlvbiBvZiBpbnRlci12YXJpYWJsZSByZWxhdGlvbnNoaXBzLiBGb2xsb3dpbmcgb3VyIHN0cmF0ZWd5IG9mIGNhdGVnb3JpemluZyB2YXJpYWJsZXMgaW50byB0aHJlZSBvdmVyYXJjaGluZyBncm91cHPigJRJbnRlcm5hbCwgU3BhdGlhbCwgYW5kIERlbW9ncmFwaGlj4oCUd2UgcHJvY2VlZGVkIHRvIGV2YWx1YXRlIGFuZCB2aXN1YWxpemUgdGhlIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIHRoZXNlIHZhcmlhYmxlcyBhbmQgdGhlIGBzYWxlX3ByaWNlYC4gQnJvYWRseSBzcGVha2luZywgd2Ugb2JzZXJ2ZWQgdGhhdCBpbnRlcm5hbCBhbmQgZGVtb2dyYXBoaWMgdmFyaWFibGVzIGV4aGliaXRlZCB0aGUgbW9zdCBwcm9ub3VuY2VkIGNvcnJlbGF0aW9ucyB3aXRoIGBzYWxlX3ByaWNlYCwgd2l0aCBvbmx5IGEgbGltaXRlZCBudW1iZXIgb2Ygc3BhdGlhbCB2YXJpYWJsZXMgZGVtb25zdHJhdGluZyBub3Rld29ydGh5IGFzc29jaWF0aW9ucyB3aXRoIHRoZSBpbmRlcGVuZGVudCB2YXJpYWJsZXMgYHNhbGVfcHJpY2VgLg0KDQojIyMgQ29ycmVsYXRpb24gTWF0cml4OiBJbnRlcm5hbCBWYXJpYWJsZXMNCg0KRm9yIHRoaXMgc3RlcCwgYSBsaXN0IG9mIGltcG9ydGFudCBpbnRlcm5hbCB2YXJpYWJsZXMgZm9yIHJlYWwgZXN0YXRlLCBsaWtlIHNhbGUgcHJpY2UsIHByb3BlcnR5IHNpemUsIGFuZCB2YXJpb3VzIGZlYXR1cmVzLCBpcyBkZWZpbmVkIGluIHRoZSAnaW50ZXJuYWx2YXJpYWJsZXMnIGxpc3QuIFRoZSBjb2RlIHRoZW4gcHJvY2Vzc2VzIHRoZSBkYXRhLCBwaWNraW5nIG91dCBvbmx5IHRoZSBudW1iZXJzIGZyb20gdGhlc2UgaW50ZXJuYWwgdmFyaWFibGVzIGFuZCByZW1vdmluZyBhbnkgcm93cyB3aXRoIG1pc3NpbmcgZGF0YS4gQWZ0ZXIgdGhhdCwgaXQgY2FsY3VsYXRlcyBob3cgdGhlc2UgaW50ZXJuYWwgdmFyaWFibGVzIGFyZSByZWxhdGVkIHRvIGVhY2ggb3RoZXIgdXNpbmcgYSBjb3JyZWxhdGlvbiBtYXRyaXguDQoNClVwb24gYW5hbHl6aW5nIHRoZSByZXN1bHRzLCB3ZSBvYnNlcnZlIHRoYXQgdmFyaWFibGVzIHN1Y2ggYXMgcHJpY2UgcGVyIHNxdWFyZSBmb290LCBudW1iZXIgb2Ygcm9vbXMsIGFuZCB0b3RhbCBsaXZhYmxlIGFyZWEgZXhoaWJpdCBzdHJvbmcgY29ycmVsYXRpb25zIHdpdGggdGhlIHNhbGUgcHJpY2UuIEhvd2V2ZXIsIGl0J3MgZXNzZW50aWFsIHRvIGtlZXAgaW4gbWluZCB0aGF0IHRoZSBwcmljZSBwZXIgc3F1YXJlIGZvb3QgaXMgZGVyaXZlZCBmcm9tIHRoZSBzYWxlIHByaWNlLCB3aGljaCBleHBsYWlucyB0aGUgaGlnaCBjb3JyZWxhdGlvbi4gU2ltaWxhcmx5LCBmb3IgdGhlIG51bWJlciBvZiByb29tcyBhbmQgbnVtYmVyIG9mIGJlZHJvb21zLCBjb25zaWRlcmluZyB0aGUgaW1wdXRhdGlvbnMgbWFkZSBlYXJsaWVyIGluIHRoaXMgYW5hbHlzaXMsIGl0IGJlY29tZXMgYSBxdWVzdGlvbmFibGUgY2hvaWNlIHRvIGluY2x1ZGUgdGhlbSBpbiB0aGUgZmluYWwgcmVncmVzc2lvbiBhbmFseXNpcyBkdWUgdG8gdGhlIGhpZ2ggbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzIGFuZCB0aGUgZ2VuZXJhbCB1bmNlcnRhaW50eSBzdXJyb3VuZGluZyB0aGUgYXNzaWduZWQgdmFsdWVzLCBlc3BlY2lhbGx5IGdpdmVuIHRoZSBoaWdoIGNvcnJlbGF0aW9uIHdpdGggYW5vdGhlciB2YXJpYWJsZSwgYHRvdGFsX2xpdmluZ19hcmVhYCwgd2hpY2ggaXMgYWxyZWFkeSBpbmNsdWRlZCBpbiB0aGUgYW5hbHlzaXMuDQoNClRoZSBmaW5hbCBpbnRlcm5hbCB2YXJpYWJsZXMgc2VsZWN0ZWQgZm9yIHRoaXMgYW5hbHlzaXMgaW5jbHVkZTogYHRvdGFsX2xpdmFibGVfYXJlYWAsYG51bWJlcl9vZl9iYXRocm9vbXNgLCBgZmlyZXBsYWNlc2AsIGBleHRlcmlvcl9jb25kaXRpb25gLCBhbmQgYGNlbnRyYWxfYWlyYC4NCg0KYGBge3IgY29yciBpbnR9DQppbnRlcm5hbHZhcmlhYmxlcyA8LSBjKA0KICAic2FsZV9wcmljZSIsICJQcmljZVBlclNxZnQiLCAidG90YWxfbGl2YWJsZV9hcmVhIiwgIm51bWJlcl9vZl9yb29tcyIsICJudW1iZXJfb2ZfYmF0aHJvb21zIiwgIm51bWJlcl9vZl9iZWRyb29tcyIsICJCdWlsZGluZ0FnZSIsICJmcm9udGFnZSIsICJkZXB0aCIsICJmaXJlcGxhY2VzIiwgIkJhc2VtZW50UHJlc2VudCIsICJHYXJhZ2VQcmVzZW50IiwgImV4dGVyaW9yX2NvbmRpdGlvbiIsICJudW1iZXJfc3RvcmllcyIsICJjZW50cmFsX2FpciINCikNCg0KbnVtZXJpY2ludFZhcnMgPC0gZGF0YSAlPiUNCiAgc3RfZHJvcF9nZW9tZXRyeShkYXRhKSAlPiUNCiAgc2VsZWN0KGludGVybmFsdmFyaWFibGVzKSU+JQ0KICBuYS5vbWl0KCkNCg0KZ2djb3JycGxvdCgNCiAgcm91bmQoY29yKG51bWVyaWNpbnRWYXJzKSwgMSksIA0KICBwLm1hdCA9IGNvcl9wbWF0KG51bWVyaWNpbnRWYXJzKSwNCiAgY29sb3JzID0gYygnI2Q3MTkxYycsJyNmZmZmYmYnLCcjMmM3YmI2JyksDQogIHR5cGU9Imxvd2VyIiwNCiAgaW5zaWcgPSAiYmxhbmsiKSArICANCiAgICBsYWJzKHRpdGxlID0gIkNvcnJlbGF0aW9uIGFjcm9zcyBJbnRlcm5hbCBWYXJpYWJsZXMiKSANCg0KYGBgDQoNCg0KIyMjIENvcnJlbGF0aW9uIE1hdHJpeDogU3BhdGlhbCBWYXJpYWJsZXMNCg0KVW5leHBlY3RlZGx5LCBkdXJpbmcgb3VyIGFuYWx5c2lzLCB3ZSBkaXNjb3ZlcmVkIHRoYXQgdGhlcmUgd2VyZSByZWxhdGl2ZWx5IGZldyBzdHJvbmcgcG9zaXRpdmUgb3IgbmVnYXRpdmUgY29ycmVsYXRpb25zIGFtb25nIHRoZSBzcGF0aWFsIGVsZW1lbnRzIHdlIGV4YW1pbmVkLiBVbmxpa2UgdGhlIGRlbW9ncmFwaGljcyBhbmQgaW50ZXJuYWwgdmFyaWFibGVzLCB3aGljaCBkZW1vbnN0cmF0ZWQgc2lnbmlmaWNhbnQgaW5mbHVlbmNlIG9uIHRoZSBzYWxlIHByaWNlLCB0aGVzZSBzcGF0aWFsIGZhY3RvcnMgc2VlbWVkIHRvIGhhdmUgbGVzcyBpbXBhY3QuIERlc3BpdGUgb3VyIGJlc3QgZWZmb3J0cyBhbmQgYXR0ZW1wdHMgdG8gYmUgaW5ub3ZhdGl2ZSBpbiBvdXIgdmFyaWFibGUgc2VsZWN0aW9uLCB0aGUgb3ZlcmFsbCBjb3JyZWxhdGlvbnMgYXBwZWFyZWQgdG8gYmUgbGVzcyBwcm9ub3VuY2VkIGluIHRoaXMgY29udGV4dC4gVGhpcyBvYnNlcnZhdGlvbiBtaWdodCBhbHNvIGJlIGxpbmtlZCB0byB0aGUgYWJzZW5jZSBvZiBlcnJvciBjbHVzdGVyaW5nIHRoYXQgd2UgaWRlbnRpZmllZCBsYXRlciBpbiBvdXIgYW5hbHlzaXMuIElmIHRpbWUgd2FzIG5vIGNvbnRyYWludCwgd2Ugd291bGQgaGF2ZSBleHBsb3JlZCBvdGhlciBzcGF0aWFsIHZhcmlibGVzIGZ1cnRoZXIsIGhvcGluZyB0byBmaW5kIG1vcmUgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBvbmVzLiAgDQoNCg0KYGBge3IgY29yciBzcGF0fQ0Kc3BhdHZhcmlhYmxlcyA8LSBjKCJzYWxlX3ByaWNlIiwgImRpc3RfdG9fbmVhcmVzdF9zY2hvb2wiLCAiZGlzdF90b19uZWFyZXN0X3B2dF9zY2hvb2wiLCAiZGlzdF90b19uZWFyZXN0X2xhbmRtYXJrIiwgImRpc3RfdG9fY29tbV9jb3JyIiwgImRpc3RfdG9fbmVhcmVzdF9zdGF0aW9uIiwgImRpc3RfdG9fbmVhcmVzdF90cmFpbCIsICJkaXN0X3RvX25lYXJlc3RfcG9saWNlX3N0YXRpb24iLCJjcmltZV9ubjUiLCAibGl0dGVyIiwgIndpdGhpbl9jb21fY29yciIsICJ3aXRoaW5fZmxvb2QiLCAiZGlzdF90b19mbG9vZHBsYWluIiwgImRpc3RfdG9fbmVhcmVzdF90ZW5uaXNjb3VydCIsICJkaXN0X3RvX25lYXJlc3RfcGxheWdyb3VuZCIsICJkaXN0X3RvX25lYXJlc3RfcG9vbCIgIA0KICApDQoNCm51bWVyaWNzcGF0VmFycyA8LSBkYXRhICU+JQ0KICBzdF9kcm9wX2dlb21ldHJ5KGRhdGEpICU+JQ0KICBzZWxlY3QoYWxsX29mKHNwYXR2YXJpYWJsZXMpKSU+JQ0KICBuYS5vbWl0KCkNCg0KZ2djb3JycGxvdCgNCiAgcm91bmQoY29yKG51bWVyaWNzcGF0VmFycyksIDEpLCANCiAgcC5tYXQgPSBjb3JfcG1hdChudW1lcmljc3BhdFZhcnMpLA0KICBjb2xvcnMgPSBjKCcjZDcxOTFjJywnI2ZmZmZiZicsJyMyYzdiYjYnKSwNCiAgdHlwZT0ibG93ZXIiLA0KICBpbnNpZyA9ICJibGFuayIpICsgIA0KICAgIGxhYnModGl0bGUgPSAiQ29ycmVsYXRpb24gYWNyb3NzIFNwYXRpYWwgVmFyaWFibGVzIikgDQoNCmBgYA0KDQojIyMgQ29ycmVsYXRpb24gTWF0cml4OiBEZW1vZ3JhcGhpYyBWYXJpYWJsZXMNCg0KDQpJbiB0aGlzIHNlY3Rpb24gb2YgdGhlIGNvZGUsIHdlIGNvbXBpbGUgYSBsaXN0IG9mIHNpZ25pZmljYW50IGRlbW9ncmFwaGljIHZhcmlhYmxlcywgaW5jbHVkaW5nIGZhY3RvcnMgc3VjaCBhcyByYWNpYWwgY29tcG9zaXRpb24sIGVkdWNhdGlvbmFsIGF0dGFpbm1lbnQsIGluY29tZSBsZXZlbHMsIGhvdXNpbmcsIGFuZCBtb3JlLiBVcG9uIGFuYWx5emluZyB0aGUgcmVzdWx0cywgdGhlIGdlbmVyYXRlZCBjb3JyZWxhdGlvbiBwbG90IGlsbHVzdHJhdGVzIGhvdyB0aGVzZSBkZW1vZ3JhcGhpYyBmYWN0b3JzIGludGVycmVsYXRlLiANCg0KSW4gb3VyIGFuYWx5c2lzLCB3ZSBvYnNlcnZlIGEgbmVhcmx5IHB1cmUgbmVnYXRpdmUgY29ycmVsYXRpb24gYmV0d2VlbiAncGVyY2VudCB3aGl0ZScgYW5kICdwZXJjZW50IGJsYWNrLicgVG8gbWl0aWdhdGUgbXVsdGljb2xsaW5lYXJpdHksIHdlIHJlY29tbWVuZCBpbmNsdWRpbmcgb25seSBvbmUgb2YgdGhlc2UgdmFyaWFibGVzIGluIHRoZSBmaW5hbCBtb2RlbC4gJ01lZGlhbiBpbmNvbWUnIGRpc3BsYXlzIGEgcm9idXN0IGFuZCBoaWdobHkgcG9zaXRpdmUgY29ycmVsYXRpb24gd2l0aCAnc2FsZSBwcmljZSwnIG1ha2luZyBpdCBhIHZhbHVhYmxlIGFkZGl0aW9uIHRvIHRoZSBtb2RlbCwgZ2l2ZW4gaXRzIHN0cm9uZyBwcmVkaWN0aXZlIHBvd2VyLiBTaW1pbGFybHksICdtb250aGx5IGNvc3QsJyB3aGljaCByZXByZXNlbnRzIHRoZSBjb3N0IG9mIHV0aWxpdGllcyBwZXIgbW9udGggaW4gaG91c2VzIHdpdGggYSBtb3J0Z2FnZSwgZXhoaWJpdHMgYSBub3Rld29ydGh5IHBvc2l0aXZlIGNvcnJlbGF0aW9uIHdpdGggJ3NhbGUgcHJpY2UsJyBtYWtpbmcgaXQgYW5vdGhlciB3b3J0aHkgaW5jbHVzaW9uIGluIHRoZSBmaW5hbCBtb2RlbCwgYXMgaXQgcHJvbWlzZXMgdG8gZW5oYW5jZSBvdXIgcHJlZGljdGl2ZSBjYXBhYmlsaXRpZXMuDQoNCmBgYHtyIGNvcnIgZGVtfQ0KZGVtdmFyaWFibGVzIDwtIGMoDQogICJzYWxlX3ByaWNlIiwgIlBjdFdoaXRlIiwgIlBjdEJsYWNrIiwgIlBjdEhpc3BhbmljIiwgIlBjdEJhY2hlbG9ycyIsICJQY3RQb3ZlcnR5IiwgIm1lZF9pbmNvbWUiLA0KICANCiAgIm1lZF9yZW50IiwgIm93bmVyX3VuaXRzIiwgICAgICAgICAicmVudGVyX3VuaXRzIiANCiAgICAgICAgLCAic2FtZV9ob3VzZV83NSIgDQogICAgICAgLCAgInNhbWVfaG91c2VfMSIgDQogICAgICAsICAgIm1vbnRobHlfY29zdHNfbm9fbW9ydGdhZ2UiIA0KICAgICAgLCAgICJtb250aGx5X2Nvc3RzX3dpdGhfbW9ydGdhZ2UiIA0KICAgICAgICAsICJtZWRfZ3Jvc3NfcmVudCIgICAgIA0KICAgICAgICAsICJob3VzaW5nX3VuaXRzX3dpdGhfaGVhdCIgDQogICAgICAgICwgImVkdV9iYWNoZWxvcnMiIA0KICAgICAgICwgICJwb3BfYmVsb3dfcG92ZXJ0eSIgDQogICAgICAgLCAgImhvdXNpbmdfdW5pdHNfaGlnaF9zcGVlZF9pbnRlcm5ldCIgDQogICAgICAgLCAgImhvdXNpbmdfdW5pdHNfbm9fdmVoaWNsZSIgICAgICAgDQogICkNCg0KbnVtZXJpY2RlbVZhcnMgPC0gZGF0YSAlPiUNCiAgc3RfZHJvcF9nZW9tZXRyeShkYXRhKSAlPiUNCiAgc2VsZWN0KGRlbXZhcmlhYmxlcyklPiUNCiAgbmEub21pdCgpDQoNCmdnY29ycnBsb3QoDQogIHJvdW5kKGNvcihudW1lcmljZGVtVmFycyksIDEpLCANCiAgcC5tYXQgPSBjb3JfcG1hdChudW1lcmljZGVtVmFycyksDQogIGNvbG9ycyA9IGMoJyNkNzE5MWMnLCcjZmZmZmJmJywnIzJjN2JiNicpLA0KICB0eXBlPSJsb3dlciIsDQogIGluc2lnID0gImJsYW5rIikgKyAgDQogICAgbGFicyh0aXRsZSA9ICJDb3JyZWxhdGlvbiBhY3Jvc3MgRGVtb2dyYXBoaWMgVmFyaWFibGVzIikgDQoNCmBgYA0KDQojIyMgRmluYWwgVmFyaWFibGUgU2VsZWN0aW9uDQoNCkJhc2VkIG9uIG91ciBpbml0aWFsIGNvcnJlbGF0aW9uIGFuYWx5c2VzLCB3ZSB3ZW50IHRocm91Z2ggYW4gaXRlcmF0aXZlIHByb2Nlc3MgdG8gZGV0ZXJtaW5lIHRoZSBmaW5hbCBzZXQgb2YgbWV0cmljcyBmb3Igb3VyIG1vZGVsLiBUaGUgY29kZSBpbml0aWFsbHkgaW5jbHVkZWQgc29tZSB2YXJpYWJsZXMgd2UgY29uc2lkZXJlZCwgYnV0IHRvIG1pbmltaXplIGVycm9ycyBsaWtlIE1BUEUgYW5kIE1BRSB3aGlsZSBtYXhpbWl6aW5nIGFjY3VyYWN5LCB3ZSBtYWRlIHNvbWUgc3Vic3RpdHV0aW9ucy4gV2UgYWxzbyBhaW1lZCB0byBlbmhhbmNlIHRoZSBtb2RlbCdzIGdlbmVyYWxpemFiaWxpdHkgYnkgY2FyZWZ1bGx5IGNob29zaW5nIGFuZCByZWplY3Rpbmcgc3BlY2lmaWMgdmFyaWFibGVzLiBGaW5hbGx5LCB3ZSBzZXR0bGVkIG9uIHRoZSB2YXJpYWJsZXMgcHJlc2VudGVkLiBUaGlzIGNvbXByZWhlbnNpdmUgYXBwcm9hY2ggYWxsb3dlZCB1cyB0byB0YWlsb3Igb3VyIG1vZGVsJ3MgdmFyaWFibGUgc2VsZWN0aW9uIHRvIG5vdCBvbmx5IHJlZHVjZSBlcnJvcnMgYnV0IGFsc28gZW5zdXJlIHRoZSBtb2RlbCdzIGFwcGxpY2FiaWxpdHkgdG8gYSB3aWRlIHJhbmdlIG9mIHNjZW5hcmlvcywgdWx0aW1hdGVseSBjb250cmlidXRpbmcgdG8gbW9yZSByb2J1c3QgYW5kIGRlcGVuZGFibGUgcmVzdWx0cyBpbiBvdXIgcmVncmVzc2lvbiBhbmFseXNpcy4NCg0KYGBge3IgY29yciBhbGx9DQoNCiN2YXJpYWJsZXMgPC0gYygic2FsZV9wcmljZSIsICJudW1iZXJfb2ZfYmF0aHJvb21zIiwiZGlzdF90b19uZWFyZXN0X3B2dF9zY2hvb2wiLCAiZGlzdF90b19jb21tX2NvcnIiLCAiZGlzdF90b19uZWFyZXN0X3N0YXRpb24iLCAiZGlzdF90b19uZWFyZXN0X3RyYWlsIiwgIm1lZF9pbmNvbWUiLCAiZGlzdF90b19uZWFyZXN0X3Bvb2wiLCAiZnJvbnRhZ2UiLCAiY2VudHJhbF9haXIiLCAiZmlyZXBsYWNlcyIsICJCYXNlbWVudFByZXNlbnQiLCAiZXh0ZXJpb3JfY29uZGl0aW9uIiwgIlBjdFdoaXRlIiwgIlBjdEJsYWNrIiwgInRvdGFsX2xpdmFibGVfYXJlYSIsICJCdWlsZGluZ0FnZSIsICAgIlBjdEJhY2hlbG9ycyIsICJQY3RQb3ZlcnR5IiwgIlByaWNlUGVyU3FmdCIsInRvdGFsX2xpdmFibGVfYXJlYSIsICJob3VzaW5nX3VuaXRzX3dpdGhfaGVhdCIsICJQY3RIaXNwYW5pYyIsICJob3VzaW5nX3VuaXRzX2hpZ2hfc3BlZWRfbnRlcm5ldCIpDQoNCnZhcmlhYmxlcyA8LSBjICgic2FsZV9wcmljZSIgLCJ0b3RhbF9saXZhYmxlX2FyZWEiLCJudW1iZXJfb2ZfYmF0aHJvb21zIiwgImZpcmVwbGFjZXMiLCAiZXh0ZXJpb3JfY29uZGl0aW9uIiwgImNlbnRyYWxfYWlyIiwgIlBjdFdoaXRlIiwgIm1lZF9pbmNvbWUiLCAibW9udGhseV9jb3N0c193aXRoX21vcnRnYWdlIiAsICJQY3RQb3ZlcnR5IiwgImRpc3RfdG9fZmxvb2RwbGFpbiIsICJ3aXRoaW5fY29tX2NvcnIiKQ0KDQojZHJvcHBpbmcgY3JpbWUgZm9yIG5vdyBjaGVjayBsYXRlciANCg0KbnVtZXJpY1ZhcnMgPC0gZGF0YSAlPiUNCiAgc3RfZHJvcF9nZW9tZXRyeShkYXRhKSAlPiUNCiAgc2VsZWN0KHZhcmlhYmxlcyklPiUNCiAgbmEub21pdCgpDQoNCmdnY29ycnBsb3QoDQogIHJvdW5kKGNvcihudW1lcmljVmFycyksIDEpLCANCiAgcC5tYXQgPSBjb3JfcG1hdChudW1lcmljVmFycyksDQogIGNvbG9ycyA9IGMoJyNkNzE5MWMnLCcjZmZmZmJmJywnIzJjN2JiNicpLA0KICB0eXBlPSJsb3dlciIsDQogIGluc2lnID0gImJsYW5rIikgKyAgDQogICAgbGFicyh0aXRsZSA9ICJDb3JyZWxhdGlvbiBhY3Jvc3MgbnVtZXJpYyB2YXJpYWJsZXMiKSANCmBgYA0KDQojIE9MUyBSZWdyZXNzc2lvbiBNb2RlbA0KDQpUaGlzIGNvZGUgdXRpbGl6ZXMgYW4gT0xTIHJlZ3Jlc3Npb24gbW9kZWwgdG8gbWFrZSBhY2N1cmF0ZSBwcmVkaWN0aW9ucyBhYm91dCBob21lIHByaWNlcy4gVGhlIGFkdmFudGFnZSBvZiBhbiBPTFMgbW9kZWwgaXMgaXRzIHNpbXBsaWNpdHkgYW5kIGVhc2Ugb2YgaW50ZXJwcmV0YXRpb24sIG1ha2luZyBpdCBhIHZhbHVhYmxlIHRvb2wgZm9yIHVuZGVyc3RhbmRpbmcgbGluZWFyIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB2YXJpYWJsZXMuDQoNCiMjIFRlc3RpbmcgYW5kIFRyYWluaW5nIERhdGEgDQoNCkZpcnN0IHdlIHNwbGl0IHRoZSBtb2RlbGxpbmcgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHMsIGVuc3VyaW5nIHRoYXQgdGhlIGRhdGEgaXMgc3BsaXQgcmFuZG9tbHkgZWFjaCB0aW1lLiANCg0KYGBge3Igc3BsaXRkYXRhfQ0KDQojIyBDcmVhdGluZyB0aGUgdHJhaW5pbmcgYW5kIHRlc3Qgc2V0DQptb2RlbGxpbmdfZGF0YSA8LSBkYXRhICU+JSBmaWx0ZXIodG9QcmVkaWN0ID09ICJNT0RFTExJTkciKQ0KDQpzZXQuc2VlZCg5OTkpICNtYWtlcyBzdXJlIGRhdGEgaXMgc3BsaXQgc2FtZSB3YXkgZXZlcnkgdGltZQ0KDQojIyBTcGxpdHRpbmcgdGhlIGRhdGENCnNwbGl0IDwtIHNhbXBsZS5zcGxpdChtb2RlbGxpbmdfZGF0YSRvYmplY3RpZCwgU3BsaXRSYXRpbyA9IDAuNzUpDQoNCiMjIENyZWF0aW5nIHRoZSB0cmFpbmluZyBhbmQgdGVzdCBzZXRzDQp0cmFpbl9kYXRhIDwtIG1vZGVsbGluZ19kYXRhW3NwbGl0LF0NCnRlc3RfZGF0YSA8LSBtb2RlbGxpbmdfZGF0YVshc3BsaXQsXQ0KYGBgDQoNCiMjIFJlZ3Jlc3Npb24gQW5hbHlzaXMNCg0KQWZ0ZXIgcnVubmluZyB0aGUgcmVncmVzc2lvbiBhbmFseXNpcywgd2Ugd2VyZSBhYmxlIHRvIGNvbWUgdG8gc29tZSBpbnRlcmVzdGluZyByZWxhdGlvbnMgYmV0d2VlbiB0aGUgc2FsZSBwcmljZSBhbmQgZGlmZmVyZW50IHZhcmlhYmxlcyB1c2VkIGluIHRoZSBhbmFseXNpcy4gDQoNCjEuIFRoZSBjb2VmZmljaWVudHMgZm9yICd0b3RhbF9saXZhYmxlX2FyZWEsJyAnbnVtYmVyX29mX2JhdGhyb29tcywnICdmaXJlcGxhY2VzLCcgJ2V4dGVyaW9yX2NvbmRpdGlvbiwnICdjZW50cmFsX2FpciwnICdQY3RXaGl0ZSwnICdtZWRfaW5jb21lLCcgJ21vbnRobHlfY29zdHNfd2l0aF9tb3J0Z2FnZSwnIGFuZCAnd2l0aGluX2NvbV9jb3JyJyBhcmUgYWxsIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQuIA0KDQoyLiBUb3RhbCBMaXZhYmxlIEFyZWEgKHRvdGFsX2xpdmFibGVfYXJlYSk6IFRoZSBjb2VmZmljaWVudCBvZiBhcHByb3hpbWF0ZWx5IDE4Mi40NCBzdWdnZXN0cyB0aGF0LCBmb3IgZWFjaCBhZGRpdGlvbmFsIHVuaXQgb2YgbGl2YWJsZSBhcmVhLCB0aGUgJ3NhbGVfcHJpY2UnIGlzIGV4cGVjdGVkIHRvIGluY3JlYXNlIGJ5IGFyb3VuZCAxODIuNDQgdW5pdHMsIHByb3ZpZGVkIHRoYXQgYWxsIG90aGVyIHZhcmlhYmxlcyByZW1haW4gY29uc3RhbnQuIFRoaXMgaW5kaWNhdGVzIGEgc3Ryb25nIHBvc2l0aXZlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBzaXplIG9mIHRoZSBwcm9wZXJ0eSBhbmQgaXRzIHNhbGUgcHJpY2UuDQoNCjMuIE51bWJlciBvZiBCYXRocm9vbXMgKG51bWJlcl9vZl9iYXRocm9vbXMpOiBXaXRoIGEgY29lZmZpY2llbnQgb2YgYXBwcm94aW1hdGVseSA1ODY1OS4wNCwgZWFjaCBhZGRpdGlvbmFsIGJhdGhyb29tIGlzIGFzc29jaWF0ZWQgd2l0aCBhIHN1YnN0YW50aWFsIGluY3JlYXNlIGluICdzYWxlX3ByaWNlLicgVGhpcyBpbXBsaWVzIHRoYXQgcHJvcGVydGllcyB3aXRoIG1vcmUgYmF0aHJvb21zIHRlbmQgdG8gY29tbWFuZCBoaWdoZXIgcHJpY2VzLg0KDQo0LiBFeHRlcmlvciBDb25kaXRpb24gKGV4dGVyaW9yX2NvbmRpdGlvbik6IFRoZSBjb2VmZmljaWVudCBvZiBhcm91bmQgLTIxNzQ5LjgwIHNpZ25pZmllcyB0aGF0LCBhcyB0aGUgY29uZGl0aW9uIG9mIHRoZSBleHRlcmlvciBkZXRlcmlvcmF0ZXMsICdzYWxlX3ByaWNlJyB0ZW5kcyB0byBkZWNyZWFzZS4gVGhpcyBuZWdhdGl2ZSBjb2VmZmljaWVudCBzdWdnZXN0cyB0aGF0IGEgd2VsbC1tYWludGFpbmVkIGV4dGVyaW9yIGhhcyBhIHBvc2l0aXZlIGltcGFjdCBvbiB0aGUgcHJvcGVydHkncyB2YWx1ZS4NCg0KNS4gTW9udGhseSBDb3N0cyB3aXRoIE1vcnRnYWdlIChtb250aGx5X2Nvc3RzX3dpdGhfbW9ydGdhZ2UpOiBUaGUgY29lZmZpY2llbnQgb2YgYXBwcm94aW1hdGVseSAxNDEuMTQgc3VnZ2VzdHMgdGhhdCBhbiBpbmNyZWFzZSBpbiB1dGlsaXR5IGNvc3RzIHBlciBtb250aCBvbiBob3VzZXMgd2l0aCBhIG1vcnRnYWdlIGxlYWRzIHRvIGEgaGlnaGVyICdzYWxlX3ByaWNlLicgVGhpcyBpbmRpY2F0ZXMgdGhhdCBidXllcnMgbWF5IGJlIHdpbGxpbmcgdG8gcGF5IG1vcmUgZm9yIHByb3BlcnRpZXMgYXNzb2NpYXRlZCB3aXRoIGhpZ2hlciBtb250aGx5IGNvc3RzLCBwb3NzaWJseSBkdWUgdG8gcGVyY2VpdmVkIHZhbHVlIG9yIGFtZW5pdGllcy4NCg0KNi4gV2l0aGluIENvbW11bml0eSBDb3JyZWxhdGlvbiAod2l0aGluX2NvbV9jb3JyKTogV2l0aCBhIGNvZWZmaWNpZW50IG9mIHJvdWdobHkgMzU2MjkuODMsIGEgaGlnaGVyIHdpdGhpbi1jb21tdW5pdHkgY29ycmVsYXRpb24gaXMgYXNzb2NpYXRlZCB3aXRoIGFuIGluY3JlYXNlIGluICdzYWxlX3ByaWNlLicgVGhpcyBpbXBsaWVzIHRoYXQgcHJvcGVydGllcyBpbiBhcmVhcyB3aGVyZSBob3VzaW5nIHByaWNlcyBhcmUgcG9zaXRpdmVseSBjb3JyZWxhdGVkIHRlbmQgdG8gY29tbWFuZCBoaWdoZXIgcHJpY2VzLCByZWZsZWN0aW5nIHRoZSBkZXNpcmFiaWxpdHkgb2Ygc3VjaCBuZWlnaGJvcmhvb2RzLg0KDQpXZSBhbHNvIHRoZW4gdXNlIHRoZSByZWdyZXNzaW9uIHRvIHByZWRpY3QgaG9tZSBwcmljZXMgZm9yIHRoZSB0ZXN0aW5nIGRhdGFzZXQsIHRvIHRoZW4gcGVyZm9ybSBhY2N1cmFjeSBhbmQgZ2VuZXJhbGl6YWJsaXR5IHRlc3RzIG9uIGl0IHRvIHJlZmluZSB0aGUgcmVncmVzc2lvbiBhbmQgY2xlYW4gYW5kIGZpbHRlciB0aGUgZGF0YXNldC4gV2UgZGlkIHRoaXMgb3ZlciBhbmQgb3ZlciwgaW4gb3JkZXIgdG8gY2hvb3NlIHRoZSB2YXJpYWJsZXMgdGhhdCBtYWRlIHRoZSBtb3N0IGFjY3VyYXRlIGFuZCBnZW5lcmFsaXphYmxlIG1vZGVsLiANCg0KDQpgYGB7ciByZWd9DQoNCiNyZWdyZXNzaW9uIG9uIHRyYWluaW5nIGRhdGENCg0KcmVnMiA8LSBsbShzYWxlX3ByaWNlIH4gLiwgZGF0YSA9IHN0X2Ryb3BfZ2VvbWV0cnkodHJhaW5fZGF0YSkgJT4lICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QodmFyaWFibGVzKSkNCg0Kc3VtbWFyeShyZWcyKQ0KDQojIyBQbG90IHJlZ3Jlc3Npb24gDQojZWZmZWN0X3Bsb3QocmVnMiwgcHJlZCA9IG51bWJlcl9vZl9iYXRocm9vbXMsIGludGVydmFsID0gVFJVRSwgcGxvdC5wb2ludHMgPSBUUlVFKQ0KcGxvdF9zdW1tcyhyZWcyLCBzY2FsZSA9IFRSVUUpDQoNCiNwcmVkaWN0aW5nIHByaWNlIGZvciB0ZXN0aW5nIGRhdGENCg0KdGVzdF9kYXRhIDwtDQogIHRlc3RfZGF0YSAlPiUNCiAgbXV0YXRlKFByaWNlLlByZWRpY3QgPSBwcmVkaWN0KHJlZzIsIHRlc3RfZGF0YSksDQogICAgICAgICBQcmljZS5FcnJvciA9IFByaWNlLlByZWRpY3QgLSBzYWxlX3ByaWNlLA0KICAgICAgICAgUHJpY2UuQWJzRXJyb3IgPSBhYnMoUHJpY2UuUHJlZGljdCAtIHNhbGVfcHJpY2UpLA0KICAgICAgICAgUHJpY2UuQVBFID0gKGFicyhQcmljZS5QcmVkaWN0IC0gc2FsZV9wcmljZSkpIC8gUHJpY2UuUHJlZGljdCkNCg0KYGBgDQoNCiMjIEVycm9yIEFuYWx5c2lzOiBBY2N1cmFjeSANCg0KVGhlIG1lYW4gYWJzb2x1dGUgZXJyb3IgKE1BRSkgZm9yIG91ciBtb2RlbCBpcyBjdXJyZW50bHkgYXBwcm94aW1hdGVseSBgJDczLDAwMGAsIGFuZCB0aGUgTWVhbiBBYnNvbHV0ZSBQZXJjZW50YWdlIEVycm9yIChNQVBFKSBzdGFuZHMgYXQgYXJvdW5kIDI4JS4gV2UgZXhwZXJpbWVudGVkIHdpdGggdmFyaW91cyB2YXJpYWJsZSBjb21iaW5hdGlvbnMgdG8gcmVkdWNlIHRoZXNlIG1ldHJpY3MsIGFuZCB3ZSBtYW5hZ2VkIHRvIGFjaGlldmUgcmVzdWx0cyBjbG9zZSB0byBgJDIwLDAwMGAgKHdpdGggYSBNQVBFIG9mIDMlKSBieSBpbmNvcnBvcmF0aW5nIHZhcmlhYmxlcyBsaWtlICJQcmljZVBlclNxRlQuIiBIb3dldmVyLCBpbmNsdWRpbmcgdGhlc2UgdmFyaWFibGVzLCB3aGljaCBhcmUgZGVyaXZlZCBmcm9tIHRoZSBwcm92aWRlZCBzYWxlX3ByaWNlLCBsZWQgdG8gYW4gb3ZlcmZpdCBtb2RlbCB0aGF0IGxhY2tlZCBnZW5lcmFsaXphYmlsaXR5IOKAkyBhIGNydWNpYWwgcmVxdWlyZW1lbnQgZm9yIG91ciBhcHBsaWNhdGlvbi4gQWRkaXRpb25hbGx5LCB0byBwcmVkaWN0IGhvbWUgcHJpY2VzIGVmZmVjdGl2ZWx5LCB3ZSBtdXN0IGF2b2lkIHVzaW5nIHZhcmlhYmxlcyBkZXJpdmVkIGZyb20gaG9tZSBwcmljZXMgdGhlbXNlbHZlcywgYXMgdGhpcyB3b3VsZCBjcmVhdGUgYSBjb252b2x1dGVkIGFuZCBzZWxmLXJlZmVyZW50aWFsIG1vZGVsLg0KDQpgYGB7cn0NCiMgdGFibGUgb2YgTUFFIGFuZCBNQVBFDQoNCiMjIEFjY3VyYWN5IC0gTWVhbiBBYnNvbHV0ZSBFcnJvcg0KTUFFIDwtIGRhdGEuZnJhbWUobWVhbih0ZXN0X2RhdGEkUHJpY2UuQWJzRXJyb3IsIG5hLnJtID0gVCkpDQpNQVBFIDwtIGRhdGEuZnJhbWUobWVhbih0ZXN0X2RhdGEkUHJpY2UuQVBFLCBuYS5ybSA9IFQpKSAjTUFQRQ0KDQoNCnJlZy5NQUUuTUFQRSA8LSBjYmluZChNQUUsIE1BUEUpICU+JQ0KICBrYWJsZShmb3JtYXQgPSAiaHRtbCIsIGNhcHRpb24gPSAiUmVncmVzc2lvbiBNQUUgJiBNQVBFIChGaWd1cmUgNS4xKSIsIGNvbC5uYW1lcyA9IGMoIk1lYW4gQWJzb2x1dGUgRXJyb3IiLCAiTWVhbiBBYnNvbHV0ZSBQZXJjZW50YWdlIEVycm9yIikpICU+JQ0KICBrYWJsZV9zdHlsaW5nKCJzdHJpcGVkIiwgZnVsbF93aWR0aCA9IEZBTFNFKQ0KDQpyZWcuTUFFLk1BUEUNCg0KYGBgDQoNCiMgR2VuZXJhbGl6YWJpbHRpeSANCg0KVXNpbmcga2ZvbGQgd2UgY2hlY2sgZ2VuZXJhbGl6YWJpbGl0eSANCg0KV2UgdXNlZCAxMDAgZm9sZHMgZm9yIGNyb3NzLXZhbGlkYXRpb24uIFRoZSBoaWdoIHZhbHVlcyBvZiBSTVNFIGluZGljYXRlIHRoYXQgdGhlIG1vZGVsIGlzIGJlaW5nIGFmZmVjdGVkIGJ5IG91dGxpZXJzIHNpZ25pZmljYW50bHkuIFRvIGFkZHJlc3MgdGhpcyBpc3N1ZSwgd2UgY29uZHVjdGVkIGFkZGl0aW9uYWwgZGF0YSBjbGVhbmluZyBhbmQgaW1wbGVtZW50ZWQgb3V0bGllciBmaWx0ZXJpbmcgdGVjaG5pcXVlcyB0byBtaW5pbWl6ZSBlcnJvcnMuIEhvd2V2ZXIsIHRoZSBhdmVyYWdlIFJNU0UgdmFsdWUgc3RpbGwgcmVtYWlucyBhYm92ZSAxMDAsMDAwLCBtYWtpbmcgaXQgcmVsYXRpdmVseSBpbmFjY3VyYXRlIGZvciBwcmVkaWN0aW5nIGhvbWUgcHJpY2VzLg0KDQpUaGUgUi1zcXVhcmVkIHN0YXRpc3RpYyByZXByZXNlbnRzIHRoZSBwcm9wb3J0aW9uIG9mIHRoZSB2YXJpYW5jZSBpbiB0aGUgc2FsZSBwcmljZXMgdGhhdCB0aGUgbW9kZWwgY2FuIGV4cGxhaW4uIEl0IHJhbmdlcyBmcm9tIDAuMyB0byAwLjksIHdpdGggYSBtZWFuIHZhbHVlIG9mIDAuNy4gQSBoaWdoZXIgUi1zcXVhcmVkIHZhbHVlIGluZGljYXRlcyBhIGJldHRlciBmaXQgb2YgdGhlIG1vZGVsIHRvIHRoZSBkYXRhLiBPbiBhdmVyYWdlLCBvdXIgbW9kZWwgY2FuIGV4cGxhaW4gYXBwcm94aW1hdGVseSA3MCUgb2YgdGhlIHZhcmlhbmNlIGluIHRoZSBkYXRhLCBpbmRpY2F0aW5nIHRoYXQgaXQgcG9zc2Vzc2VzIGEgcmVhc29uYWJsZSBsZXZlbCBvZiBnZW5lcmFsaXphYmlsaXR5Lg0KDQpgYGB7cn0NCg0KdGVzdF9kYXRhIDwtDQogIHRlc3RfZGF0YSAlPiUNCiAgZmlsdGVyKHNhbGVfcHJpY2UgPCAxMDAwMDAwMCkNCg0KIyMgSy1Gb2xkOiBHZW5lcmFsaXphYmlsaXR5IENyb3NzLVZhbGlkYXRpb24NCg0KZml0Q29udHJvbCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgbnVtYmVyID0gMTAwKQ0Kc2V0LnNlZWQoOTk5KQ0KDQpyZWcuY3YgPC0gDQogIHRyYWluKHNhbGVfcHJpY2UgfiAuLCBkYXRhID0gc3RfZHJvcF9nZW9tZXRyeSh0ZXN0X2RhdGEpICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdCh2YXJpYWJsZXMpLA0KbWV0aG9kID0gImxtIiwgdHJDb250cm9sID0gZml0Q29udHJvbCwgbmEuYWN0aW9uID0gbmEucGFzcykNCg0Kc3RhcmdhemVyKGFzLmRhdGEuZnJhbWUocmVnLmN2JHJlc2FtcGxlKSwgdHlwZT0idGV4dCIsIGRpZ2l0cz0xLCB0aXRsZT0iQ3Jvc3MgVmFsaWRhdGlvbiBSZXN1bHRzICg1LjIpIiwgb3V0ID0gIkNWLnR4dCIpICNhbGwgY3YNCg0KYGBgDQoNCk1vc3Qgb2Ygb3VyIE1BRSBzZWVtIHRvIGJlIHdpdGhpbiB0aGUgNjAsMDAwIGRvbGxhciB0byA4MCwwMDAgZG9sbGFyIHJhbmdlICh3aXRoIGEgbWVhbiBvZiA3MSwwMDApIHdoaWNoIGlzIGlsbHVzdHJhdGVkIGluIHRoZSBmb2xsb3dpbmcgaGlzdG9ncmFtLiAgDQoNCmBgYHtyfQ0KI3Bsb3R0aW5nIHRoZSBjcm9zcyB2YWxpZGF0aW9uIHN0dWZmDQoNCmdncGxvdChyZWcuY3YkcmVzYW1wbGUsIGFlcyh4PU1BRSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICIjMmM3YmI2IikgKw0KICBsYWJzKHRpdGxlID0gIkNyb3NzIFZhbGlkYXRpb24gVGVzdHMgaW4gTWVhbiBBdmVyYWdlIEVycm9yIiwgY2FwdGlvbj0iRmlndXJlIDUuMyIpICsNCiAgcGxvdFRoZW1lKCkNCg0KYGBgDQpUaGUgcHJlZGljdGVkIGFuZCBvYnNlcnZlZCBwcmljZXMgbG9vayBldmVubHkgZGlzdHJpYnV0ZWQgYWJvdmUgYW5kIGJlbG93IHRoZSBwZXJmZWN0IHByZWRpY3Rpb24gbGluZSwgYmVsb3cgdGhlICQ1MCwwMDAgbGluZS4gT3VyIG1vZGVsIG1vdmVzIGJlbG93IHRoZSBwZXJmZWN0IHByZWRpY3Rpb24gbGluZSBiZXlvbmQgNTAsMDAwIGRvbGxhcnMgaW5kaWNhdGluZyB0aGF0IG91ciBtb2RlbCBtYXkgYmUgdW5kZXItcHJlZGljdGluZyB0aGUgdmFsdWVzIG9mIGhpZ2ggdmFsdWUgaG9tZXMuIA0KDQoNCmBgYHtyfQ0KDQp0ZXN0X2RhdGEgJT4lDQogIGRwbHlyOjpzZWxlY3QoUHJpY2UuUHJlZGljdCwgc2FsZV9wcmljZSkgJT4lDQogICAgZ2dwbG90KGFlcyhzYWxlX3ByaWNlLCBQcmljZS5QcmVkaWN0KSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBzdGF0X3Ntb290aChhZXMoc2FsZV9wcmljZSwgc2FsZV9wcmljZSksIA0KICAgICAgICAgICAgIG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UsIHNpemUgPSAxLCBjb2xvdXI9IiNkNzE5MWMiKSArIA0KICBzdGF0X3Ntb290aChhZXMoUHJpY2UuUHJlZGljdCwgc2FsZV9wcmljZSksIA0KICAgICAgICAgICAgICBtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFLCBzaXplID0gMSwgY29sb3VyPSIjMmM3YmI2IikgKw0KICBsYWJzKHRpdGxlPSJQcmVkaWN0ZWQgU2FsZSBQcmljZSBhcyBhIEZ1bmN0aW9uIG9mIE9ic2VydmVkIFByaWNlIiwNCiAgICAgICBzdWJ0aXRsZT0iUmVkIGxpbmUgcmVwcmVzZW50cyBhIHBlcmZlY3QgcHJlZGljdGlvbjsgQmx1ZSBsaW5lIHJlcHJlc2VudHMgcHJlZGljdGlvbiIsDQogICAgICAgY2FwdGlvbiA9ICJGaWd1cmUgNi4yIiwNCiAgICAgICB4ID0gIk9ic2VydmVkIFByaWNlIiwNCiAgICAgICB5ID0gIlByZWRpY3RlZCBQcmljZSIpICsNCiAgcGxvdFRoZW1lKCkrIHhsaW0oMCwgMjAwMDAwMCkgKyB5bGltKDAsIDIwMDAwMDApDQpgYGANCg0KIyBTcGF0aWFsIENvcnJlbGF0aW9ucyANCg0KYGBge3J9DQoNCiN1c2UgdGhlIHNhbWUgdGhuZyBmb3Igd2lnaHRzIGFuZCBtb3JhbnMgaSANCiNtb3ZlIHRoZSBmaWx0ZXJzIHRvIHRoaXMgY2VsIHRvbyANCg0KDQojIFNwYXRpYWwgTGFnIGZvciBwcmljZQ0KY29vcmRzIDwtIHN0X2Nvb3JkaW5hdGVzKHRlc3RfZGF0YSkgDQpuZWlnaGJvckxpc3QgPC0ga25uMm5iKGtuZWFybmVpZ2goY29vcmRzLCA1KSkNCnNwYXRpYWxXZWlnaHRzIDwtIG5iMmxpc3R3KG5laWdoYm9yTGlzdCwgc3R5bGU9IlciKQ0KdGVzdF9kYXRhJGxhZ1ByaWNlIDwtIGxhZy5saXN0dyhzcGF0aWFsV2VpZ2h0cywgdGVzdF9kYXRhJHNhbGVfcHJpY2UpDQoNCnRlc3RfZGF0YSRsYWdQcmljZUVycm9yIDwtIGxhZy5saXN0dyhzcGF0aWFsV2VpZ2h0cywgdGVzdF9kYXRhJFByaWNlLkVycm9yLCBOQU9LID0gVFJVRSkNCg0KIyBGaWx0ZXJpbmcgZ3JlYXRlciB0aGFuIDAgdmFsdWVzDQoNCnRlc3RfZGF0YV9maWx0ZXIgPC0gdGVzdF9kYXRhICU+JQ0KICBmaWx0ZXIoUHJpY2UuRXJyb3IgPiAwLCBsYWdQcmljZUVycm9yID4gMCkNCg0KZ2dwbG90KGRhdGEgPSB0ZXN0X2RhdGFfZmlsdGVyLCBhZXMobGFnUHJpY2VFcnJvciwgUHJpY2UuRXJyb3IpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IC44NSxjb2xvdXIgPSAiYmxhY2siKSArIA0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLGNvbG91ciA9ICJyZWQiLHNpemUgPSAxLjIpICsNCiAgbGFicyh0aXRsZT0iUHJpY2UgRXJyb3JzIiwNCiAgICAgICBjYXB0aW9uID0gIkZpZ3VyZSA2LjEiKSArDQogIHBsb3RUaGVtZSgpDQoNCmBgYA0KIyMgTW9yYW4ncyBJIA0KDQpXZSBvYnNlcnZlIGEgbG93IE1vcmFuJ3MgSSB2YWx1ZSwgaW5kaWNhdGluZyB0aGF0IGVycm9ycyBhcmUgZGlzcGVyc2VkIGFjcm9zcyBvdXIgZGF0YS4gVGhpcyBzdWdnZXN0cyB0aGF0IHRoZXJlIGlzIG5vIHNpZ25pZmljYW50IGNsdXN0ZXJpbmcgb2YgZXJyb3JzLCBhIHBhdHRlcm4gdGhhdCBpcyBhbHNvIGV2aWRlbnQgaW4gdGhlIG1hcC4gQ29uc2VxdWVudGx5LCBpdCBpcyBwcm9iYWJsZSB0aGF0IGVycm9ycyBpbiBvdXIgbW9kZWwgbWF5IHN0ZW0gZnJvbSBvdGhlciBkZW1vZ3JhcGhpYyBvciBpbnRlcm5hbCB2YXJpYWJsZXMgdGhhdCB3ZSBtYXkgbm90IGhhdmUgYWNjb3VudGVkIGZvci4gVG8gaW1wcm92ZSB0aGUgYWNjdXJhY3kgb2Ygb3VyIGFuYWx5c2lzLCBmdXJ0aGVyIGludmVzdGlnYXRpb24gYW5kIGluY2x1c2lvbiBvZiB0aGVzZSBwb3RlbnRpYWxseSBvbWl0dGVkIHZhcmlhYmxlcyBtaWdodCBiZSBuZWNlc3NhcnkuDQoNCg0KYGBge3J9DQoNCiMgZm9yIG1vcmFucyAxDQoNCiMgU3BhdGlhbCBMYWcgZm9yIHByaWNlDQpjb29yZHMgPC0gc3RfY29vcmRpbmF0ZXModGVzdF9kYXRhX2ZpbHRlcikgDQpuZWlnaGJvckxpc3QgPC0ga25uMm5iKGtuZWFybmVpZ2goY29vcmRzLCA1KSkNCnNwYXRpYWxXZWlnaHRzIDwtIG5iMmxpc3R3KG5laWdoYm9yTGlzdCwgc3R5bGU9IlciKQ0KdGVzdF9kYXRhX2ZpbHRlciRsYWdQcmljZSA8LSBsYWcubGlzdHcoc3BhdGlhbFdlaWdodHMsIHRlc3RfZGF0YV9maWx0ZXIkc2FsZV9wcmljZSkNCg0KdGVzdF9kYXRhX2ZpbHRlciRsYWdQcmljZUVycm9yIDwtIGxhZy5saXN0dyhzcGF0aWFsV2VpZ2h0cywgdGVzdF9kYXRhX2ZpbHRlciRQcmljZS5FcnJvciwgTkFPSyA9IFRSVUUpDQoNCg0KDQptb3JhblRlc3QgPC0gbW9yYW4ubWMobmEub21pdCh0ZXN0X2RhdGFfZmlsdGVyJFByaWNlLkVycm9yKSwNCiAgICAgICAgICAgICAgICAgICAgICBzcGF0aWFsV2VpZ2h0cywgbnNpbSA9IDk5OSkgIA0KDQpnZ3Bsb3QoYXMuZGF0YS5mcmFtZShtb3JhblRlc3QkcmVzW2MoMTo5OTkpXSksIGFlcyhtb3JhblRlc3QkcmVzW2MoMTo5OTkpXSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjAwNSkgKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbW9yYW5UZXN0JHN0YXRpc3RpYyksIGNvbG91ciA9ICIjZDcxOTFjIixzaXplPTEpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoLTAuNSwwLjUpKSArDQogIGxhYnModGl0bGU9Ik9ic2VydmVkIGFuZCBwZXJtdXRlZCBNb3JhbidzIEkiLA0KICAgICAgIHN1YnRpdGxlPSAiT2JzZXJ2ZWQgTW9yYW4ncyBJIGluIHJlZCIsDQogICAgICAgY2FwdGlvbiA9ICJGaWd1cmUgNi4zIiwNCiAgICAgICB4PSJNb3JhbidzIEkiLA0KICAgICAgIHk9IkNvdW50IikgKw0KICBwbG90VGhlbWUoKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSB0cmFjdHMyMSwgZmlsbCA9ICJncmV5ODkiLCBjb2xvciA9ICJncmV5ODkiKSArDQogIGdlb21fc2YoZGF0YSA9IHRlc3RfZGF0YSwgYWVzKGNvbG91ciA9IHE1KFByaWNlLkFic0Vycm9yKSksIA0KICAgICAgICAgIHNob3cubGVnZW5kID0gInBvaW50Iiwgc2l6ZSA9IC43NSkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGVlLA0KICAgICAgICAgICAgICAgICAgIGxhYmVscz1xQnIodGVzdF9kYXRhLCJQcmljZS5BYnNFcnJvciIpLA0KICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlXG5CcmVha3MiKSArDQogIGxhYnModGl0bGU9Ik1hcCBvZiBQcmljZSBBYnNvbHV0ZSBFcnJvcnMgZm9yIFRlc3QgU2V0IiwgDQogICAgICAgc3VidGl0bGU9ICJQaGlsYWRlbHBoaWEiLA0KICAgICAgY2FwdGlvbj0iRmlndXJlIDYuNCIpICsNCiAgbWFwVGhlbWUoKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSB0cmFjdHMyMSwgZmlsbCA9ICJncmV5ODkiLCBjb2xvciA9ICJncmV5ODkiKSArDQogIGdlb21fc2YoZGF0YSA9IHRlc3RfZGF0YSwgYWVzKGNvbG91ciA9IHE1KFByaWNlLkFic0Vycm9yKSksIA0KICAgICAgICAgIHNob3cubGVnZW5kID0gInBvaW50Iiwgc2l6ZSA9IC43NSkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGVlLA0KICAgICAgICAgICAgICAgICAgIGxhYmVscz1xQnIodGVzdF9kYXRhLCJQcmljZS5QcmVkaWN0IiksDQogICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGVcbkJyZWFrcyIpICsNCiAgbGFicyh0aXRsZT0iUHJlZGljdGVkIFNhbGUgUHJpY2UgZm9yIFRlc3QgU2V0IiwgDQogICAgICAgc3VidGl0bGUgPSAiUGhpbGFkZWxwaGlhIiwNCiAgICAgIGNhcHRpb249IkZpZ3VyZSA2LjQiKSArDQogIG1hcFRoZW1lKCkNCmBgYA0KIyBEaWZmZXJpbmcgQ29udGV4dHMgDQoNCkhlcmUgd2UgdHJ5IHRvIG9ic2VydmUgdHJlbmRzIG9mIGVycm9yIGFuZCBwcmVkaWN0aW9ucyB3aXRoaW4gZWFjaCBjZW5zdXMgdHJhY3QuIFdlIGNhbiBzZWUgdGhhdCB0aGUgTUFQRSBsaWVzIHdpdGhpbiBhIDAgdG8gMSByYW5nZSwgYW5kIGlzIGhpZ2hlciBpbiBjZXJ0YWluIGNlbnN1cyB0cmFjdHMgaW4gTm9ydGggUGhpbGFkZWxwaGlhLCBpbmRpY2F0aW5nIGEgcG90ZW50aWFsIG1pc3Npbmcgc3BhdGlhbCBmYWN0b3IuIA0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KDQojdGhpcyBpcyB3cm9uZyANCg0KbGlicmFyeShzZikNCg0KdGVzdF9kYXRhICU+JQ0KICBncm91cF9ieShjZW5zdXNfdHJhY3QpICU+JQ0KICBzdW1tYXJpemUoTUFQRSA9IG1lYW4oUHJpY2UuQVBFLCBuYS5ybSA9IFQpKSAlPiUNCiAgdW5ncm91cCgpIA0KDQpzdF9qb2luKHRyYWN0czIxLCB0ZXN0X2RhdGEpJT4lDQogICMgIHN0X3NmKCkgJT4lDQogICAgZ2dwbG90KCkgKyANCiAgIGdlb21fc2YoYWVzKGZpbGwgPSBNQVBFKSkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50MigNCiAgICBsb3cgPSAiIzJjN2JiNiIsICAgIyBDb2xvciBmb3IgdmFsdWVzIDwgLTENCiAgICBtaWQgPSAiI2ZmZmZiZiIsICAjIENvbG9yIGZvciB2YWx1ZXMgYmV0d2VlbiAtMSBhbmQgKzENCiAgICBoaWdoID0gIiNkNzE5MWMiLCAgICMgQ29sb3IgZm9yIHZhbHVlcyA+ICsxDQogICAgbWlkcG9pbnQgPSAwLCAgICMgU2V0IHRoZSBtaWRwb2ludCBhdCAwDQogICAgbGltaXRzID0gYygwLCAxKSwgICMgRGVmaW5lIHRoZSBsaW1pdHMNCiAgICBicmVha3MgPSBjKDAsIDAuMiwgMC40LCAwLjgsIDEpLCAgIyBTcGVjaWZ5IGJyZWFrIHBvaW50cw0KICAgIGxhYmVscyA9IGMoIjw9IDAiLCAiMCB0byAwLjIiLCAiMC4yIHRvIDAuNCIsICIwLjQgdG8gMC44IiwgIj49IDEiKSwgICMgTGFiZWxzIGZvciBicmVha3MNCiAgICBuYW1lID0gIk1BUEUgQ2xhc3NlcyIgICMgTGVnZW5kIHRpdGxlDQogICkgICsNCiAgZ2VvbV9zZihkYXRhID0gdGVzdF9kYXRhLCBjb2xvdXIgPSAiYmxhY2siLCBhbHBoYSA9IDAuNSwgIHNob3cubGVnZW5kID0gInBvaW50Iiwgc2l6ZSA9IC41KSArDQogICAgICBsYWJzKHRpdGxlID0gIk1lYW4gdGVzdCBzZXQgTUFQRSBieSBDZW5zdXMgVHJhY3QiLA0KICAgICAgICAgICBjYXB0aW9uID0gIkZpZ3VyZSA3LjMiKSArDQogICAgICBtYXBUaGVtZSgpDQoNCmBgYA0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KIyBGaXJzdCwgam9pbiB0aGUgZGF0YSBmcmFtZXMNCmpvaW5lZF9kYXRhIDwtIHN0X2pvaW4odHJhY3RzMjEsIHRlc3RfZGF0YSkNCg0KIyBUaGVuLCBjYWxjdWxhdGUgTUFQRQ0KbWFwZV9kYXRhIDwtIGpvaW5lZF9kYXRhICU+JQ0KICBncm91cF9ieShjZW5zdXNfdHJhY3QpICU+JQ0KICBzdW1tYXJpemUoTUFQRSA9IG1lYW4oUHJpY2UuQVBFLCBuYS5ybSA9IFRSVUUpKSAlPiUNCiAgdW5ncm91cCgpDQoNCiMgQ3JlYXRlIHRoZSBwbG90DQpnZ3Bsb3QoKSArIA0KICBnZW9tX3NmKGRhdGEgPSBqb2luZWRfZGF0YSwgYWVzKGZpbGwgPSBNQVBFKSwgc2l6ZSA9IDAuNSkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50MigNCiAgICBsb3cgPSAiIzJjN2JiNiIsDQogICAgbWlkID0gIiNmZmZmYmYiLA0KICAgIGhpZ2ggPSAiI2Q3MTkxYyIsDQogICAgbWlkcG9pbnQgPSAwLA0KICAgIGxpbWl0cyA9IGMoMCwgMSksDQogICAgYnJlYWtzID0gYygwLCAwLjIsIDAuNCwgMC44LCAxKSwNCiAgICBsYWJlbHMgPSBjKCI8PSAwIiwgIjAgdG8gMC4yIiwgIjAuMiB0byAwLjQiLCAiMC40IHRvIDAuOCIsICI+PSAxIiksDQogICAgbmFtZSA9ICJNQVBFIENsYXNzZXMiDQogICkgKw0KICBnZW9tX3NmKGRhdGEgPSBtYXBlX2RhdGEsIGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjUsIHNpemUgPSAwLjUpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJNZWFuIHRlc3Qgc2V0IE1BUEUgYnkgQ2Vuc3VzIFRyYWN0IiwNCiAgICBjYXB0aW9uID0gIkZpZ3VyZSA3LjMiDQogICkgKw0KICBtYXBUaGVtZSgpDQpgYGANCg0KYGBge3J9DQoNCnRlc3RfZGF0YSA8LQ0KICB0ZXN0X2RhdGEgJT4lDQogIGdyb3VwX2J5KGNlbnN1c190cmFjdCkgJT4lDQogIG11dGF0ZShNZWFuUHJpY2UgPSBtZWFuKHNhbGVfcHJpY2UpKQ0KDQp0ZXN0X2RhdGEgPC0NCiAgdGVzdF9kYXRhICU+JQ0KICBncm91cF9ieShjZW5zdXNfdHJhY3QpICU+JQ0KICBtdXRhdGUoTUFQRSA9IG1lYW4oUHJpY2UuQVBFKSkgJT4lDQogIGZpbHRlcihNQVBFPiAwKQ0KDQpnZ3Bsb3QodGVzdF9kYXRhKSArDQogIGdlb21fcG9pbnQoYWVzKE1lYW5QcmljZSwgTUFQRSkpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgYWVzKE1lYW5QcmljZSwgTUFQRSksIHNlID0gRkFMU0UsIGNvbG91ciA9ICJncmVlbiIpICsNCiAgbGFicyh0aXRsZSA9ICJNQVBFIGJ5IENlbnN1cyBUcmFjdCBhcyBhIGZ1bmN0aW9uIG9mIE1lYW4gUHJpY2UgYnkgQ2Vuc3VzIFRyYWN0IiwNCiAgICAgICB4ID0gIk1lYW4gSG9tZSBQcmljZSIsIHkgPSAiTUFQRSIsDQogICAgICAgY2FwdGlvbiA9ICJGaWd1cmUgNy41IikgKw0KICBwbG90VGhlbWUoKQ0KYGBgDQojIyBUaWR5Y2Vuc3VzIA0KDQojIERpc2N1c3Npb24gDQoNCkFzc2Vzc2luZyB0aGUgbW9kZWwncyBlZmZlY3RpdmVuZXNzIGlzIGEgbnVhbmNlZCB0YXNrLiBXaGlsZSBpdCBleGhpYml0cyBtb2RlcmF0ZSBnZW5lcmFsaXphYmlsaXR5LCB0aGUgcHJlc2VuY2Ugb2Ygbm90YWJseSBoaWdoIGVycm9ycyByYWlzZXMgY29uY2VybnMgYWJvdXQgaXRzIHByZWRpY3RpdmUgYWNjdXJhY3kuIE9uZSBpbnRyaWd1aW5nIGZpbmRpbmcgd2FzIHRoZSBsYWNrIG9mIHNwYXRpYWwgY29ycmVsYXRpb24gd2l0aCBzYWxlIHByaWNlcywgZGVmeWluZyB0aGUgaW50dWl0aXZlIGV4cGVjdGF0aW9uIHRoYXQgcHJvcGVydGllcyBuZWFyIGxhbmRtYXJrcyBvciBwcml2YXRlIHNjaG9vbHMgd291bGQgY29tbWFuZCBoaWdoZXIgcHJpY2VzLiBXZSB3ZXJlIGFibGUgdG8gcHJlZGljdCBhcHByb3hpbWF0ZWx5IDcwJSBvZiB0aGUgdmFyaWFuY2UgaW4gc2FsZSBwcmljZXMsIHdoaWNoIGlzIGEgcmVhc29uYWJsZSBsZXZlbCBvZiBleHBsYW5hdG9yeSBwb3dlciBmb3IgYSByZWdyZXNzaW9uIG1vZGVsLg0KDQpVbmRlcnN0YW5kaW5nIHRoZSBlcnJvcnMgaW4gcHJlZGljdGlvbnMgaXMgY29tcGxpY2F0ZWQgYnkgdGhlaXIgZGlzcGVyc2VkIG5hdHVyZSBhbmQgdGhlIGFic2VuY2Ugb2YgY2x1c3RlcmluZy4gVGhlIGxvdyBNb3JhbidzIEkgc3RhdGlzdGljIGluZGljYXRlcyBhIGxhY2sgb2Ygc3BhdGlhbCBhdXRvY29ycmVsYXRpb24sIG1ha2luZyBpdCBjaGFsbGVuZ2luZyB0byBhdHRyaWJ1dGUgZXJyb3JzIHRvIHNwZWNpZmljIGdlb2dyYXBoaWMgcGF0dGVybnMuIFRoZSBpbmZsdWVuY2Ugb2YgZGVtb2dyYXBoaWMgZmFjdG9ycywgYXMgb3V0bGluZWQgZWFybGllciwgbWF5IGJlIGNvbnRyaWJ1dGluZyB0byB0aGUgb2JzZXJ2ZWQgZXJyb3JzLg0KDQpXaGVuIGl0IGNvbWVzIHRvIHNwYXRpYWwgdmFyaWF0aW9uLCB0aGUgZGlzcGVyc2VkIGVycm9ycyBtYWtlIGl0IGRpZmZpY3VsdCB0byBpZGVudGlmeSBzcGVjaWZpYyBhcmVhcyB3aGVyZSB0aGUgbW9kZWwgcGVyZm9ybWVkIGV4Y2VwdGlvbmFsbHkgd2VsbCBvciBwb29ybHkuIEhvd2V2ZXIsIGl0J3Mgd29ydGggbm90aW5nIHRoYXQgdGhlcmUgaXMgYSBoaWdoZXIgZXJyb3IgY291bnQgaW4gU291dGggYW5kIENlbnRlciBDaXR5LCBzdWdnZXN0aW5nIHRoZSBwb3NzaWJpbGl0eSBvZiB1bmFjY291bnRlZCBzcGF0aWFsIGZhY3RvcnMgaW4gdGhvc2UgcmVnaW9ucy4NCg0KIyBQcmVkaWN0aW9uIENoYWxsZW5nZSANCg0KQWxsIHRob3NlIHRoaW5ncyBjb25zaWRlcmVkIHdlIHVzZWQgdGhlIG1vZGVsIHRvIHByZWRpY3QgaG9tZSBwcmljZXMgZm9yIHRoZSBjaGFsbGVuZ2UgZGF0YSBzZXQuIFdlIG5vdGljZSB0aGF0IHRoZSBudW1iZXIgaGFzIGdvbmUgZG93biBmcm9tIDEwMCB0byA3OSwgYnV0IHdlIGtub3cgdGhhdCB0aGlzIGlzIGFzIGEgY29uc2VxdWVuY2Ugb2Ygb3VyIGRhdGEgY2xlYW5pbmcgYW5kIHJlbW92aW5nIGRhdGEgcG9pbnRzIHdpdGggbWlzc2luZyBvciBpbmNvbXBsZXRlIHZhbHVlcyB0aGF0IGNvdWxkIG5vdCBiZSBpbXB1dGVkIChzdWNoIGFzIHllYXIgYnVpbHQpLiANCg0KYGBge3J9DQoNCiNmb3IgcHJlZGljdGlvbiANCg0KcmVnMSA8LSBsbShzYWxlX3ByaWNlIH4gLiwgZGF0YSA9IHN0X2Ryb3BfZ2VvbWV0cnkoZGF0YV9vZykgJT4lICANCiAgICAgICAgICAgICBkcGx5cjo6ZmlsdGVyKHRvUHJlZGljdCA9PSAiTU9ERUxMSU5HIikgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KHZhcmlhYmxlcykpDQogIA0KZGF0YV9wcmVkIDwtDQogIGRhdGFfb2cgJT4lDQogIG11dGF0ZShQcmljZS5QcmVkaWN0ID0gcHJlZGljdChyZWcxLGRhdGFfb2cpKQ0KDQpmaW5hbF9wcmVkIDwtIHN0X2Ryb3BfZ2VvbWV0cnkoZGF0YV9wcmVkKSU+JQ0KICAgZmlsdGVyKHRvUHJlZGljdD09ICJDSEFMTEVOR0UiKSU+JQ0KICBzZWxlY3QobXVzYUlELFByaWNlLlByZWRpY3QpJT4lDQogIG11dGF0ZSh0ZWFtID0gIlNhbSBhbmQgUm9zaGluaSIpDQoNCndyaXRlLmNzdihmaW5hbF9wcmVkLCAiLi9vdXRwdXRzL3NraGFyZV9yZ2FuZXNoX2NoYWxsZW5nZS5jc3YiKQ0KDQojbWFwIA0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImdyZXk4OSIpICsNCiAgZ2VvbV9zZihkYXRhID0gZGF0YV9wcmVkLCBhZXMoY29sb3VyID0gcTUoUHJpY2UuUHJlZGljdCkpLCANCiAgICAgICAgICBzaG93LmxlZ2VuZCA9ICJwb2ludCIsIHNpemUgPSAuNzUpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlZSwNCiAgICAgICAgICAgICAgICAgICBsYWJlbHM9cUJyKGRhdGFfcHJlZCwiUHJpY2UuUHJlZGljdCIpLA0KICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlXG5CcmVha3MiKSArDQogIGxhYnModGl0bGU9IlByZWRpY3RlZCBTYWxlIFByaWNlIGZvciBhbGwgRGF0YSIsIA0KICAgICAgIHN1YnRpdGxlID0gIlBoaWxhZGVscGhpYSIpICsNCiAgbWFwVGhlbWUoKQ0KDQoNCmBgYA0KDQojIENvbmNsdXNpb24NCg0KV2Ugd291bGQgcmVjb21tZW5kIHRoaXMgbW9kZWwgdG8gWmlsbG93IGlmIHRoZSBlcnJvcnMgd2VyZW4ndCBzbyBoaWdoLiBUaGUgbW9kZWwgcGVyZm9ybXMgd2VsbCBpbiBtYW55IGNvbnRleHRzIGJ1dCBpZiB0aW1lIHdhcyBubyBib3VuZCwgd2Ugd291bGQgaGF2ZSByZWFsbHkgbGlrZWQgdG8gZXhwbG9yZSBtb3JlIHZhcmlhYmxlcyBhbmQgcmVmaW5lIGl0IG1vcmUgdG8gbWluaW1pemUgdGhlIGVycm9ycyBiZWZvcmUgc2hhcmluZyB0aGUgbW9kZWwuIA0KDQo=